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,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Definition Loader
|
|
3
|
+
*
|
|
4
|
+
* Parses agent definitions from agents/*.md files.
|
|
5
|
+
* These definitions are used to construct extraSystemPrompt for subagent runs.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
/**
|
|
10
|
+
* Parse markdown file with YAML frontmatter
|
|
11
|
+
*/
|
|
12
|
+
function parseMarkdown(content) {
|
|
13
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
14
|
+
if (!match) {
|
|
15
|
+
return { frontmatter: {}, body: content };
|
|
16
|
+
}
|
|
17
|
+
const frontmatter = {};
|
|
18
|
+
const frontmatterText = match[1];
|
|
19
|
+
const body = match[2];
|
|
20
|
+
// Simple YAML-like parsing
|
|
21
|
+
let currentKey = '';
|
|
22
|
+
let currentArray = null;
|
|
23
|
+
frontmatterText.split('\n').forEach(line => {
|
|
24
|
+
// Array item
|
|
25
|
+
if (line.match(/^\s*-\s+/)) {
|
|
26
|
+
if (currentArray !== null) {
|
|
27
|
+
const value = line.replace(/^\s*-\s+/, '').trim();
|
|
28
|
+
currentArray.push(value);
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Key-value pair
|
|
33
|
+
const colonIndex = line.indexOf(':');
|
|
34
|
+
if (colonIndex > 0) {
|
|
35
|
+
const key = line.slice(0, colonIndex).trim();
|
|
36
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
37
|
+
if (value === '') {
|
|
38
|
+
// Start of array
|
|
39
|
+
currentKey = key;
|
|
40
|
+
currentArray = [];
|
|
41
|
+
frontmatter[key] = currentArray;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Simple value
|
|
45
|
+
currentKey = '';
|
|
46
|
+
currentArray = null;
|
|
47
|
+
frontmatter[key] = value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return { frontmatter, body };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the agents directory path
|
|
55
|
+
* Handles both development and installed scenarios
|
|
56
|
+
*/
|
|
57
|
+
function resolveAgentsDir() {
|
|
58
|
+
// Try multiple locations
|
|
59
|
+
const possiblePaths = [
|
|
60
|
+
// Development: relative to dist/core/
|
|
61
|
+
path.resolve(__dirname, '../../agents'),
|
|
62
|
+
// Installed: relative to extension root
|
|
63
|
+
path.resolve(__dirname, '../agents'),
|
|
64
|
+
// Absolute fallback
|
|
65
|
+
path.resolve(process.cwd(), 'agents'),
|
|
66
|
+
];
|
|
67
|
+
for (const p of possiblePaths) {
|
|
68
|
+
if (fs.existsSync(p)) {
|
|
69
|
+
return p;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Return default (may not exist)
|
|
73
|
+
return possiblePaths[0];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Load agent definition by name
|
|
77
|
+
*
|
|
78
|
+
* @param name - Agent name (e.g., 'explorer', 'diagnostician')
|
|
79
|
+
* @returns Agent definition or null if not found
|
|
80
|
+
*/
|
|
81
|
+
export function loadAgentDefinition(name) {
|
|
82
|
+
const agentsDir = resolveAgentsDir();
|
|
83
|
+
const mdPath = path.join(agentsDir, `${name}.md`);
|
|
84
|
+
if (!fs.existsSync(mdPath)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const content = fs.readFileSync(mdPath, 'utf8');
|
|
89
|
+
const { frontmatter, body } = parseMarkdown(content);
|
|
90
|
+
// Extract tools as array (support both string and array formats)
|
|
91
|
+
let tools;
|
|
92
|
+
if (Array.isArray(frontmatter.tools)) {
|
|
93
|
+
tools = frontmatter.tools;
|
|
94
|
+
}
|
|
95
|
+
else if (typeof frontmatter.tools === 'string') {
|
|
96
|
+
tools = frontmatter.tools.split(',').map(s => s.trim()).filter(Boolean);
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
name: frontmatter.name || name,
|
|
100
|
+
description: frontmatter.description || '',
|
|
101
|
+
systemPrompt: body.trim(),
|
|
102
|
+
tools,
|
|
103
|
+
model: frontmatter.model,
|
|
104
|
+
permissionMode: frontmatter.permissionMode,
|
|
105
|
+
skills: frontmatter.skills,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
console.error(`[AgentLoader] Failed to load agent ${name}:`, err);
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* List all available agents
|
|
115
|
+
*
|
|
116
|
+
* @returns Array of agent names
|
|
117
|
+
*/
|
|
118
|
+
export function listAvailableAgents() {
|
|
119
|
+
const agentsDir = resolveAgentsDir();
|
|
120
|
+
if (!fs.existsSync(agentsDir)) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const files = fs.readdirSync(agentsDir);
|
|
125
|
+
return files
|
|
126
|
+
.filter(f => f.endsWith('.md'))
|
|
127
|
+
.map(f => path.basename(f, '.md'));
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Load all agent definitions
|
|
135
|
+
*
|
|
136
|
+
* @returns Map of agent name to definition
|
|
137
|
+
*/
|
|
138
|
+
export function loadAllAgents() {
|
|
139
|
+
const agents = new Map();
|
|
140
|
+
for (const name of listAvailableAgents()) {
|
|
141
|
+
const def = loadAgentDefinition(name);
|
|
142
|
+
if (def) {
|
|
143
|
+
agents.set(name, def);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return agents;
|
|
147
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PainConfig } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Singleton service to manage the Pain Configuration.
|
|
4
|
+
*/
|
|
5
|
+
export declare const ConfigService: {
|
|
6
|
+
/**
|
|
7
|
+
* Gets or initializes the Pain Configuration instance.
|
|
8
|
+
* @param stateDir The directory where the settings JSON is stored.
|
|
9
|
+
*/
|
|
10
|
+
get(stateDir: string): PainConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Resets the singleton instance (primarily for testing).
|
|
13
|
+
*/
|
|
14
|
+
reset(): void;
|
|
15
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PainConfig } from './config.js';
|
|
2
|
+
let config = null;
|
|
3
|
+
let lastStateDir = null;
|
|
4
|
+
/**
|
|
5
|
+
* Singleton service to manage the Pain Configuration.
|
|
6
|
+
*/
|
|
7
|
+
export const ConfigService = {
|
|
8
|
+
/**
|
|
9
|
+
* Gets or initializes the Pain Configuration instance.
|
|
10
|
+
* @param stateDir The directory where the settings JSON is stored.
|
|
11
|
+
*/
|
|
12
|
+
get(stateDir) {
|
|
13
|
+
if (!config || lastStateDir !== stateDir) {
|
|
14
|
+
config = new PainConfig(stateDir);
|
|
15
|
+
config.load();
|
|
16
|
+
lastStateDir = stateDir;
|
|
17
|
+
}
|
|
18
|
+
return config;
|
|
19
|
+
},
|
|
20
|
+
/**
|
|
21
|
+
* Resets the singleton instance (primarily for testing).
|
|
22
|
+
*/
|
|
23
|
+
reset() {
|
|
24
|
+
config = null;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export interface DeepReflectionSettings {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
mode: 'auto' | 'forced' | 'disabled';
|
|
4
|
+
force_checkpoint?: boolean;
|
|
5
|
+
checkpoint_message?: string;
|
|
6
|
+
auto_trigger_conditions?: {
|
|
7
|
+
min_tool_calls?: number;
|
|
8
|
+
error_rate_threshold?: number;
|
|
9
|
+
complexity_keywords?: string[];
|
|
10
|
+
};
|
|
11
|
+
default_model?: string;
|
|
12
|
+
default_depth?: number;
|
|
13
|
+
timeout_ms?: number;
|
|
14
|
+
modelsDir?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface TrustSettings {
|
|
17
|
+
stages: {
|
|
18
|
+
stage_1_observer: number;
|
|
19
|
+
stage_2_editor: number;
|
|
20
|
+
stage_3_developer: number;
|
|
21
|
+
};
|
|
22
|
+
cold_start: {
|
|
23
|
+
initial_trust: number;
|
|
24
|
+
grace_failures: number;
|
|
25
|
+
cold_start_period_ms: number;
|
|
26
|
+
};
|
|
27
|
+
penalties: {
|
|
28
|
+
tool_failure_base: number;
|
|
29
|
+
risky_failure_base: number;
|
|
30
|
+
gate_bypass_attempt: number;
|
|
31
|
+
failure_streak_multiplier: number;
|
|
32
|
+
max_penalty: number;
|
|
33
|
+
};
|
|
34
|
+
rewards: {
|
|
35
|
+
success_base: number;
|
|
36
|
+
subagent_success: number;
|
|
37
|
+
tool_success_reward: number;
|
|
38
|
+
streak_bonus_threshold: number;
|
|
39
|
+
streak_bonus: number;
|
|
40
|
+
recovery_boost: number;
|
|
41
|
+
max_reward: number;
|
|
42
|
+
};
|
|
43
|
+
limits: {
|
|
44
|
+
stage_2_max_lines: number;
|
|
45
|
+
stage_3_max_lines: number;
|
|
46
|
+
};
|
|
47
|
+
history_limit?: number;
|
|
48
|
+
}
|
|
49
|
+
export interface PainSettings {
|
|
50
|
+
language: 'en' | 'zh';
|
|
51
|
+
thresholds: {
|
|
52
|
+
pain_trigger: number;
|
|
53
|
+
cognitive_paralysis_input: number;
|
|
54
|
+
stuck_loops_trigger: number;
|
|
55
|
+
semantic_min_score: number;
|
|
56
|
+
promotion_count_threshold: number;
|
|
57
|
+
promotion_similarity_threshold: number;
|
|
58
|
+
};
|
|
59
|
+
scores: {
|
|
60
|
+
paralysis: number;
|
|
61
|
+
default_confusion: number;
|
|
62
|
+
default_loop: number;
|
|
63
|
+
tool_failure_friction: number;
|
|
64
|
+
exit_code_penalty: number;
|
|
65
|
+
spiral_penalty: number;
|
|
66
|
+
missing_test_command_penalty: number;
|
|
67
|
+
subagent_error_penalty: number;
|
|
68
|
+
subagent_timeout_penalty: number;
|
|
69
|
+
};
|
|
70
|
+
severity_thresholds: {
|
|
71
|
+
high: number;
|
|
72
|
+
medium: number;
|
|
73
|
+
low: number;
|
|
74
|
+
};
|
|
75
|
+
intervals: {
|
|
76
|
+
worker_poll_ms: number;
|
|
77
|
+
initial_delay_ms: number;
|
|
78
|
+
task_timeout_ms: number;
|
|
79
|
+
};
|
|
80
|
+
trust: TrustSettings;
|
|
81
|
+
deep_reflection?: DeepReflectionSettings;
|
|
82
|
+
}
|
|
83
|
+
export declare const DEFAULT_SETTINGS: PainSettings;
|
|
84
|
+
export declare class PainConfig {
|
|
85
|
+
private settings;
|
|
86
|
+
private filePath;
|
|
87
|
+
constructor(stateDir: string);
|
|
88
|
+
load(): void;
|
|
89
|
+
save(): void;
|
|
90
|
+
private deepMerge;
|
|
91
|
+
/**
|
|
92
|
+
* Basic validation for critical settings
|
|
93
|
+
*/
|
|
94
|
+
private validate;
|
|
95
|
+
/**
|
|
96
|
+
* Gets a value using dot notation (e.g. 'thresholds.pain_trigger')
|
|
97
|
+
*/
|
|
98
|
+
get(keyPath: string): any;
|
|
99
|
+
/**
|
|
100
|
+
* Returns all settings as a plain object.
|
|
101
|
+
*/
|
|
102
|
+
getAll(): PainSettings;
|
|
103
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
// 🚀 THE "IT JUST WORKS" DEFAULT SETTINGS 🚀
|
|
5
|
+
// These defaults have been carefully tuned to ensure that a
|
|
6
|
+
// new user installing this plugin for the first time will NOT
|
|
7
|
+
// be constantly blocked by the security gate. The AI is given
|
|
8
|
+
// a high initial trust score (Developer stage) and penalties
|
|
9
|
+
// are forgiving, encouraging exploration rather than paralysis.
|
|
10
|
+
// ─────────────────────────────────────────────────────────────
|
|
11
|
+
export const DEFAULT_SETTINGS = {
|
|
12
|
+
language: 'zh', // Optimized for the primary user base
|
|
13
|
+
thresholds: {
|
|
14
|
+
pain_trigger: 40, // Increased tolerance before forcing a stop
|
|
15
|
+
cognitive_paralysis_input: 4000,
|
|
16
|
+
stuck_loops_trigger: 4, // Allow more retries before calling it a loop
|
|
17
|
+
semantic_min_score: 0.7,
|
|
18
|
+
promotion_count_threshold: 3,
|
|
19
|
+
promotion_similarity_threshold: 0.8
|
|
20
|
+
},
|
|
21
|
+
scores: {
|
|
22
|
+
paralysis: 30, // Reduced from 40
|
|
23
|
+
default_confusion: 30,
|
|
24
|
+
default_loop: 40,
|
|
25
|
+
tool_failure_friction: 15, // Reduced from 30. A failing tool shouldn't instantly cripple the AI
|
|
26
|
+
exit_code_penalty: 50, // Reduced from 70
|
|
27
|
+
spiral_penalty: 30,
|
|
28
|
+
missing_test_command_penalty: 20,
|
|
29
|
+
subagent_error_penalty: 60,
|
|
30
|
+
subagent_timeout_penalty: 50
|
|
31
|
+
},
|
|
32
|
+
severity_thresholds: {
|
|
33
|
+
high: 70,
|
|
34
|
+
medium: 40,
|
|
35
|
+
low: 20
|
|
36
|
+
},
|
|
37
|
+
intervals: {
|
|
38
|
+
worker_poll_ms: 15 * 60 * 1000,
|
|
39
|
+
initial_delay_ms: 5000,
|
|
40
|
+
task_timeout_ms: 30 * 60 * 1000
|
|
41
|
+
},
|
|
42
|
+
trust: {
|
|
43
|
+
stages: {
|
|
44
|
+
stage_1_observer: 30,
|
|
45
|
+
stage_2_editor: 60,
|
|
46
|
+
stage_3_developer: 80,
|
|
47
|
+
},
|
|
48
|
+
cold_start: {
|
|
49
|
+
// 🚀 The most important change: Start at 85 (Developer level)
|
|
50
|
+
// This allows the AI to perform medium-sized edits right out of the box
|
|
51
|
+
// without needing to beg for a PLAN.md on every single change.
|
|
52
|
+
initial_trust: 85,
|
|
53
|
+
grace_failures: 5, // Give the AI 5 free mistakes before deducting any trust points
|
|
54
|
+
cold_start_period_ms: 24 * 60 * 60 * 1000,
|
|
55
|
+
},
|
|
56
|
+
penalties: {
|
|
57
|
+
// 🛡️ Forgiving penalties for exploration
|
|
58
|
+
tool_failure_base: -2, // Was -8. A simple 'ls' typo shouldn't cost 8 points.
|
|
59
|
+
risky_failure_base: -10, // Was -15.
|
|
60
|
+
gate_bypass_attempt: -5,
|
|
61
|
+
failure_streak_multiplier: -2,
|
|
62
|
+
max_penalty: -20,
|
|
63
|
+
},
|
|
64
|
+
rewards: {
|
|
65
|
+
success_base: 2, // Was 1. Faster recovery
|
|
66
|
+
subagent_success: 5, // Was 3.
|
|
67
|
+
tool_success_reward: 0.2, // 👈 Minor reward for tool success, but resets streak!
|
|
68
|
+
streak_bonus_threshold: 3, // Was 5. Easier to get bonuses
|
|
69
|
+
streak_bonus: 5,
|
|
70
|
+
recovery_boost: 5, // Was 3. If trust drops low, it's easier to climb back up
|
|
71
|
+
max_reward: 15,
|
|
72
|
+
},
|
|
73
|
+
limits: {
|
|
74
|
+
stage_2_max_lines: 50, // Was 10. 10 lines is barely enough to fix a function signature.
|
|
75
|
+
stage_3_max_lines: 300, // Was 100. Allow substantial feature implementation.
|
|
76
|
+
},
|
|
77
|
+
history_limit: 50
|
|
78
|
+
},
|
|
79
|
+
deep_reflection: {
|
|
80
|
+
enabled: true,
|
|
81
|
+
mode: 'auto',
|
|
82
|
+
force_checkpoint: true,
|
|
83
|
+
checkpoint_message: 'Before responding, quick self-check: 1. Task complexity (simple/medium/complex) 2. Information sufficiency (sufficient/need more) 3. If complex or insufficient info, call deep_reflect tool',
|
|
84
|
+
auto_trigger_conditions: {
|
|
85
|
+
min_tool_calls: 5,
|
|
86
|
+
error_rate_threshold: 0.3,
|
|
87
|
+
complexity_keywords: ['refactor', 'architecture', 'design', 'optimize', 'security', 'critical']
|
|
88
|
+
},
|
|
89
|
+
default_model: 'T-01',
|
|
90
|
+
default_depth: 2,
|
|
91
|
+
timeout_ms: 60000
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
export class PainConfig {
|
|
95
|
+
settings = { ...DEFAULT_SETTINGS };
|
|
96
|
+
filePath;
|
|
97
|
+
constructor(stateDir) {
|
|
98
|
+
this.filePath = path.join(stateDir, 'pain_settings.json');
|
|
99
|
+
}
|
|
100
|
+
load() {
|
|
101
|
+
if (fs.existsSync(this.filePath)) {
|
|
102
|
+
try {
|
|
103
|
+
const loaded = JSON.parse(fs.readFileSync(this.filePath, 'utf8'));
|
|
104
|
+
this.settings = this.deepMerge(DEFAULT_SETTINGS, loaded);
|
|
105
|
+
this.validate(this.settings);
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
console.error('[PD] Failed to parse pain_settings.json, using defaults.');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(`[PD:Config] Settings not found at ${this.filePath}, creating with defaults`);
|
|
113
|
+
this.save();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
save() {
|
|
117
|
+
try {
|
|
118
|
+
const dir = path.dirname(this.filePath);
|
|
119
|
+
if (!fs.existsSync(dir)) {
|
|
120
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
fs.writeFileSync(this.filePath, JSON.stringify(this.settings, null, 2), 'utf8');
|
|
123
|
+
console.log(`[PD:Config] Settings saved to ${this.filePath}`);
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
console.error(`[PD:Config] Failed to save settings: ${String(e)}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
deepMerge(target, source) {
|
|
130
|
+
const output = { ...target };
|
|
131
|
+
if (source && typeof source === 'object') {
|
|
132
|
+
Object.keys(source).forEach(key => {
|
|
133
|
+
// 👈 FIX: Skip if source value is undefined to avoid overwriting defaults
|
|
134
|
+
if (source[key] === undefined)
|
|
135
|
+
return;
|
|
136
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
137
|
+
if (!(key in target)) {
|
|
138
|
+
Object.assign(output, { [key]: source[key] });
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
output[key] = this.deepMerge(target[key], source[key]);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
Object.assign(output, { [key]: source[key] });
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return output;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Basic validation for critical settings
|
|
153
|
+
*/
|
|
154
|
+
validate(settings) {
|
|
155
|
+
// Ensure trust scores stay within 0-100 logical range
|
|
156
|
+
const s = settings.trust.stages;
|
|
157
|
+
if (s.stage_1_observer < 0 || s.stage_1_observer > 100)
|
|
158
|
+
s.stage_1_observer = 30;
|
|
159
|
+
if (s.stage_2_editor < 0 || s.stage_2_editor > 100)
|
|
160
|
+
s.stage_2_editor = 60;
|
|
161
|
+
if (s.stage_3_developer < 0 || s.stage_3_developer > 100)
|
|
162
|
+
s.stage_3_developer = 80;
|
|
163
|
+
// Ensure intervals are positive
|
|
164
|
+
if (settings.intervals.worker_poll_ms < 1000)
|
|
165
|
+
settings.intervals.worker_poll_ms = 15 * 60 * 1000;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Gets a value using dot notation (e.g. 'thresholds.pain_trigger')
|
|
169
|
+
*/
|
|
170
|
+
get(keyPath) {
|
|
171
|
+
const parts = keyPath.split('.');
|
|
172
|
+
let current = this.settings;
|
|
173
|
+
for (const part of parts) {
|
|
174
|
+
if (current === undefined || current[part] === undefined)
|
|
175
|
+
return undefined;
|
|
176
|
+
current = current[part];
|
|
177
|
+
}
|
|
178
|
+
return current;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Returns all settings as a plain object.
|
|
182
|
+
*/
|
|
183
|
+
getAll() {
|
|
184
|
+
return this.settings;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PainDictionary } from './dictionary.js';
|
|
2
|
+
export interface DetectionResult {
|
|
3
|
+
detected: boolean;
|
|
4
|
+
severity?: number;
|
|
5
|
+
ruleId?: string;
|
|
6
|
+
source: 'l1_exact' | 'l2_cache' | 'l3_async_queued' | 'l3_semantic_hit';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Orchestrates the three-layer detection funnel for pain signals.
|
|
10
|
+
*/
|
|
11
|
+
export declare class DetectionFunnel {
|
|
12
|
+
private dictionary;
|
|
13
|
+
private cache;
|
|
14
|
+
private asyncQueue;
|
|
15
|
+
constructor(dictionary: PainDictionary);
|
|
16
|
+
/**
|
|
17
|
+
* Detects pain in the given text using L1 (Exact), L2 (Cache), and L3 (Async).
|
|
18
|
+
*/
|
|
19
|
+
detect(text: string): DetectionResult;
|
|
20
|
+
private computeHash;
|
|
21
|
+
private enqueueAsync;
|
|
22
|
+
/**
|
|
23
|
+
* Internal method for the worker to update the cache after a semantic hit.
|
|
24
|
+
*/
|
|
25
|
+
updateCache(text: string, result: {
|
|
26
|
+
detected: boolean;
|
|
27
|
+
severity?: number;
|
|
28
|
+
}): void;
|
|
29
|
+
/**
|
|
30
|
+
* Retrieves and clears the current asynchronous queue.
|
|
31
|
+
*/
|
|
32
|
+
flushQueue(): string[];
|
|
33
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
/**
|
|
3
|
+
* A simple LRU Cache implementation using Map.
|
|
4
|
+
*/
|
|
5
|
+
class SimpleLRU {
|
|
6
|
+
maxSize;
|
|
7
|
+
cache;
|
|
8
|
+
constructor(maxSize = 100) {
|
|
9
|
+
this.maxSize = maxSize;
|
|
10
|
+
this.cache = new Map();
|
|
11
|
+
}
|
|
12
|
+
get(key) {
|
|
13
|
+
const item = this.cache.get(key);
|
|
14
|
+
if (item !== undefined) {
|
|
15
|
+
// Refresh: delete and re-insert
|
|
16
|
+
this.cache.delete(key);
|
|
17
|
+
this.cache.set(key, item);
|
|
18
|
+
}
|
|
19
|
+
return item;
|
|
20
|
+
}
|
|
21
|
+
set(key, value) {
|
|
22
|
+
if (this.cache.has(key)) {
|
|
23
|
+
this.cache.delete(key);
|
|
24
|
+
}
|
|
25
|
+
else if (this.cache.size >= this.maxSize) {
|
|
26
|
+
// Remove the oldest (first) item
|
|
27
|
+
const firstKey = this.cache.keys().next().value;
|
|
28
|
+
if (firstKey !== undefined) {
|
|
29
|
+
this.cache.delete(firstKey);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
this.cache.set(key, value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Orchestrates the three-layer detection funnel for pain signals.
|
|
37
|
+
*/
|
|
38
|
+
export class DetectionFunnel {
|
|
39
|
+
dictionary;
|
|
40
|
+
cache = new SimpleLRU(100);
|
|
41
|
+
asyncQueue = [];
|
|
42
|
+
constructor(dictionary) {
|
|
43
|
+
this.dictionary = dictionary;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Detects pain in the given text using L1 (Exact), L2 (Cache), and L3 (Async).
|
|
47
|
+
*/
|
|
48
|
+
detect(text) {
|
|
49
|
+
// --- Layer 1: Exact Match (Sync) ---
|
|
50
|
+
const exactMatch = this.dictionary.match(text);
|
|
51
|
+
if (exactMatch) {
|
|
52
|
+
return {
|
|
53
|
+
detected: true,
|
|
54
|
+
severity: exactMatch.severity,
|
|
55
|
+
ruleId: exactMatch.ruleId,
|
|
56
|
+
source: 'l1_exact'
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// --- Layer 2: LRU Cache (Sync) ---
|
|
60
|
+
const hash = this.computeHash(text);
|
|
61
|
+
const cached = this.cache.get(hash);
|
|
62
|
+
if (cached) {
|
|
63
|
+
return {
|
|
64
|
+
detected: cached.detected,
|
|
65
|
+
severity: cached.severity,
|
|
66
|
+
source: 'l2_cache'
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// --- Layer 3: Async Semantic Queue ---
|
|
70
|
+
this.enqueueAsync(text);
|
|
71
|
+
return {
|
|
72
|
+
detected: false,
|
|
73
|
+
source: 'l3_async_queued'
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
computeHash(text) {
|
|
77
|
+
return createHash('sha256').update(text).digest('hex');
|
|
78
|
+
}
|
|
79
|
+
enqueueAsync(text) {
|
|
80
|
+
if (this.asyncQueue.length < 1000) {
|
|
81
|
+
this.asyncQueue.push(text);
|
|
82
|
+
}
|
|
83
|
+
// Worker will pick this up and perform semantic search via createMemorySearchTool
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Internal method for the worker to update the cache after a semantic hit.
|
|
87
|
+
*/
|
|
88
|
+
updateCache(text, result) {
|
|
89
|
+
const hash = this.computeHash(text);
|
|
90
|
+
this.cache.set(hash, result);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Retrieves and clears the current asynchronous queue.
|
|
94
|
+
*/
|
|
95
|
+
flushQueue() {
|
|
96
|
+
const queue = [...this.asyncQueue];
|
|
97
|
+
this.asyncQueue = [];
|
|
98
|
+
return queue;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DetectionFunnel } from './detection-funnel.js';
|
|
2
|
+
/**
|
|
3
|
+
* Singleton service to manage the Semantic Detection Funnel.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DetectionService: {
|
|
6
|
+
/**
|
|
7
|
+
* Gets or initializes the Detection Funnel instance.
|
|
8
|
+
* @param stateDir The directory used to initialize the dictionary.
|
|
9
|
+
*/
|
|
10
|
+
get(stateDir: string): DetectionFunnel;
|
|
11
|
+
/**
|
|
12
|
+
* Resets the singleton instance (primarily for testing).
|
|
13
|
+
*/
|
|
14
|
+
reset(): void;
|
|
15
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DetectionFunnel } from './detection-funnel.js';
|
|
2
|
+
import { DictionaryService } from './dictionary-service.js';
|
|
3
|
+
let instance = null;
|
|
4
|
+
let lastStateDir = null;
|
|
5
|
+
/**
|
|
6
|
+
* Singleton service to manage the Semantic Detection Funnel.
|
|
7
|
+
*/
|
|
8
|
+
export const DetectionService = {
|
|
9
|
+
/**
|
|
10
|
+
* Gets or initializes the Detection Funnel instance.
|
|
11
|
+
* @param stateDir The directory used to initialize the dictionary.
|
|
12
|
+
*/
|
|
13
|
+
get(stateDir) {
|
|
14
|
+
if (!instance || lastStateDir !== stateDir) {
|
|
15
|
+
const dictionary = DictionaryService.get(stateDir);
|
|
16
|
+
instance = new DetectionFunnel(dictionary);
|
|
17
|
+
lastStateDir = stateDir;
|
|
18
|
+
}
|
|
19
|
+
return instance;
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* Resets the singleton instance (primarily for testing).
|
|
23
|
+
*/
|
|
24
|
+
reset() {
|
|
25
|
+
instance = null;
|
|
26
|
+
lastStateDir = null;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PainDictionary } from './dictionary.js';
|
|
2
|
+
/**
|
|
3
|
+
* Singleton service to manage the Pain Dictionary.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DictionaryService: {
|
|
6
|
+
/**
|
|
7
|
+
* Gets or initializes the Pain Dictionary instance.
|
|
8
|
+
* @param stateDir The directory where the dictionary JSON is stored.
|
|
9
|
+
*/
|
|
10
|
+
get(stateDir: string): PainDictionary;
|
|
11
|
+
/**
|
|
12
|
+
* Resets the singleton instance (primarily for testing).
|
|
13
|
+
*/
|
|
14
|
+
reset(): void;
|
|
15
|
+
};
|