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.
Files changed (189) hide show
  1. package/dist/commands/capabilities.d.ts +3 -0
  2. package/dist/commands/capabilities.js +73 -0
  3. package/dist/commands/evolver.d.ts +9 -0
  4. package/dist/commands/evolver.js +26 -0
  5. package/dist/commands/pain.d.ts +5 -0
  6. package/dist/commands/pain.js +114 -0
  7. package/dist/commands/strategy.d.ts +3 -0
  8. package/dist/commands/strategy.js +29 -0
  9. package/dist/commands/thinking-os.d.ts +2 -0
  10. package/dist/commands/thinking-os.js +162 -0
  11. package/dist/commands/trust.d.ts +4 -0
  12. package/dist/commands/trust.js +95 -0
  13. package/dist/core/agent-loader.d.ts +44 -0
  14. package/dist/core/agent-loader.js +147 -0
  15. package/dist/core/config-service.d.ts +15 -0
  16. package/dist/core/config-service.js +26 -0
  17. package/dist/core/config.d.ts +103 -0
  18. package/dist/core/config.js +186 -0
  19. package/dist/core/detection-funnel.d.ts +33 -0
  20. package/dist/core/detection-funnel.js +100 -0
  21. package/dist/core/detection-service.d.ts +15 -0
  22. package/dist/core/detection-service.js +28 -0
  23. package/dist/core/dictionary-service.d.ts +15 -0
  24. package/dist/core/dictionary-service.js +26 -0
  25. package/dist/core/dictionary.d.ts +36 -0
  26. package/dist/core/dictionary.js +136 -0
  27. package/dist/core/event-log.d.ts +53 -0
  28. package/dist/core/event-log.js +196 -0
  29. package/dist/core/evolution-engine.d.ts +119 -0
  30. package/dist/core/evolution-engine.js +542 -0
  31. package/dist/core/evolution-types.d.ts +126 -0
  32. package/dist/core/evolution-types.js +56 -0
  33. package/dist/core/hygiene/tracker.d.ts +22 -0
  34. package/dist/core/hygiene/tracker.js +106 -0
  35. package/dist/core/init.d.ts +12 -0
  36. package/dist/core/init.js +117 -0
  37. package/dist/core/migration.d.ts +6 -0
  38. package/dist/core/migration.js +90 -0
  39. package/dist/core/pain.d.ts +4 -0
  40. package/dist/core/pain.js +70 -0
  41. package/dist/core/path-resolver.d.ts +43 -0
  42. package/dist/core/path-resolver.js +259 -0
  43. package/dist/core/paths.d.ts +60 -0
  44. package/dist/core/paths.js +67 -0
  45. package/dist/core/profile.d.ts +62 -0
  46. package/dist/core/profile.js +210 -0
  47. package/dist/core/risk-calculator.d.ts +7 -0
  48. package/dist/core/risk-calculator.js +39 -0
  49. package/dist/core/session-tracker.d.ts +76 -0
  50. package/dist/core/session-tracker.js +286 -0
  51. package/dist/core/system-logger.d.ts +8 -0
  52. package/dist/core/system-logger.js +31 -0
  53. package/dist/core/trust-engine.d.ts +91 -0
  54. package/dist/core/trust-engine.js +284 -0
  55. package/dist/core/workspace-context.d.ts +64 -0
  56. package/dist/core/workspace-context.js +134 -0
  57. package/dist/hooks/gate.d.ts +6 -0
  58. package/dist/hooks/gate.js +487 -0
  59. package/dist/hooks/lifecycle.d.ts +5 -0
  60. package/dist/hooks/lifecycle.js +180 -0
  61. package/dist/hooks/llm.d.ts +4 -0
  62. package/dist/hooks/llm.js +153 -0
  63. package/dist/hooks/pain.d.ts +5 -0
  64. package/dist/hooks/pain.js +173 -0
  65. package/dist/hooks/prompt.d.ts +38 -0
  66. package/dist/hooks/prompt.js +285 -0
  67. package/dist/hooks/subagent.d.ts +2 -0
  68. package/dist/hooks/subagent.js +70 -0
  69. package/dist/i18n/commands.d.ts +26 -0
  70. package/dist/i18n/commands.js +88 -0
  71. package/dist/index.d.ts +7 -0
  72. package/dist/index.js +204 -0
  73. package/dist/service/evolution-worker.d.ts +17 -0
  74. package/dist/service/evolution-worker.js +293 -0
  75. package/dist/tools/agent-spawn.d.ts +33 -0
  76. package/dist/tools/agent-spawn.js +170 -0
  77. package/dist/tools/critique-prompt.d.ts +14 -0
  78. package/dist/tools/critique-prompt.js +81 -0
  79. package/dist/tools/deep-reflect.d.ts +19 -0
  80. package/dist/tools/deep-reflect.js +174 -0
  81. package/dist/tools/model-index.d.ts +9 -0
  82. package/dist/tools/model-index.js +82 -0
  83. package/dist/types/event-types.d.ts +229 -0
  84. package/dist/types/event-types.js +73 -0
  85. package/dist/types/hygiene-types.d.ts +20 -0
  86. package/dist/types/hygiene-types.js +12 -0
  87. package/dist/types.d.ts +1 -0
  88. package/dist/types.js +1 -0
  89. package/dist/utils/file-lock.d.ts +64 -0
  90. package/dist/utils/file-lock.js +270 -0
  91. package/dist/utils/glob-match.d.ts +28 -0
  92. package/dist/utils/glob-match.js +49 -0
  93. package/dist/utils/hashing.d.ts +9 -0
  94. package/dist/utils/hashing.js +25 -0
  95. package/dist/utils/io.d.ts +6 -0
  96. package/dist/utils/io.js +106 -0
  97. package/dist/utils/nlp.d.ts +9 -0
  98. package/dist/utils/nlp.js +59 -0
  99. package/dist/utils/plugin-logger.d.ts +39 -0
  100. package/dist/utils/plugin-logger.js +70 -0
  101. package/openclaw.plugin.json +46 -0
  102. package/package.json +63 -0
  103. package/templates/langs/en/core/AGENTS.md +206 -0
  104. package/templates/langs/en/core/BOOT.md +60 -0
  105. package/templates/langs/en/core/BOOTSTRAP.md +250 -0
  106. package/templates/langs/en/core/HEARTBEAT.md +74 -0
  107. package/templates/langs/en/core/IDENTITY.md +8 -0
  108. package/templates/langs/en/core/PRINCIPLES.md +10 -0
  109. package/templates/langs/en/core/SOUL.md +76 -0
  110. package/templates/langs/en/core/TOOLS.md +53 -0
  111. package/templates/langs/en/core/USER.md +10 -0
  112. package/templates/langs/en/pain/00_seed_samples.md +23 -0
  113. package/templates/langs/en/pain_dictionary.json +22 -0
  114. package/templates/langs/en/skills/admin/SKILL.md +40 -0
  115. package/templates/langs/en/skills/bootstrap-tools/SKILL.md +53 -0
  116. package/templates/langs/en/skills/deductive-audit/SKILL.md +36 -0
  117. package/templates/langs/en/skills/evolution-framework-update/SKILL.md +31 -0
  118. package/templates/langs/en/skills/evolve-system/SKILL.md +46 -0
  119. package/templates/langs/en/skills/evolve-task/SKILL.md +83 -0
  120. package/templates/langs/en/skills/feedback/SKILL.md +51 -0
  121. package/templates/langs/en/skills/init-strategy/SKILL.md +54 -0
  122. package/templates/langs/en/skills/inject-rule/SKILL.md +19 -0
  123. package/templates/langs/en/skills/manage-okr/SKILL.md +96 -0
  124. package/templates/langs/en/skills/pain/SKILL.md +19 -0
  125. package/templates/langs/en/skills/pd-daily/SKILL.md +199 -0
  126. package/templates/langs/en/skills/pd-grooming/SKILL.md +46 -0
  127. package/templates/langs/en/skills/pd-mentor/SKILL.md +230 -0
  128. package/templates/langs/en/skills/plan-script/SKILL.md +32 -0
  129. package/templates/langs/en/skills/profile/SKILL.md +24 -0
  130. package/templates/langs/en/skills/reflection/SKILL.md +40 -0
  131. package/templates/langs/en/skills/reflection-log/SKILL.md +37 -0
  132. package/templates/langs/en/skills/report/SKILL.md +13 -0
  133. package/templates/langs/en/skills/root-cause/SKILL.md +33 -0
  134. package/templates/langs/en/skills/triage/SKILL.md +29 -0
  135. package/templates/langs/en/skills/watch-evolution/SKILL.md +33 -0
  136. package/templates/langs/zh/core/AGENTS.md +207 -0
  137. package/templates/langs/zh/core/BOOT.md +60 -0
  138. package/templates/langs/zh/core/BOOTSTRAP.md +250 -0
  139. package/templates/langs/zh/core/HEARTBEAT.md +74 -0
  140. package/templates/langs/zh/core/IDENTITY.md +8 -0
  141. package/templates/langs/zh/core/SOUL.md +76 -0
  142. package/templates/langs/zh/core/TOOLS.md +53 -0
  143. package/templates/langs/zh/core/USER.md +10 -0
  144. package/templates/langs/zh/pain/00_seed_samples.md +24 -0
  145. package/templates/langs/zh/pain_dictionary.json +18 -0
  146. package/templates/langs/zh/skills/admin/SKILL.md +42 -0
  147. package/templates/langs/zh/skills/bootstrap-tools/SKILL.md +52 -0
  148. package/templates/langs/zh/skills/deductive-audit/SKILL.md +36 -0
  149. package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +31 -0
  150. package/templates/langs/zh/skills/evolve-system/SKILL.md +46 -0
  151. package/templates/langs/zh/skills/evolve-task/SKILL.md +83 -0
  152. package/templates/langs/zh/skills/feedback/SKILL.md +53 -0
  153. package/templates/langs/zh/skills/init-strategy/SKILL.md +54 -0
  154. package/templates/langs/zh/skills/inject-rule/SKILL.md +19 -0
  155. package/templates/langs/zh/skills/manage-okr/SKILL.md +109 -0
  156. package/templates/langs/zh/skills/pain/SKILL.md +19 -0
  157. package/templates/langs/zh/skills/pd-daily/SKILL.md +199 -0
  158. package/templates/langs/zh/skills/pd-grooming/SKILL.md +46 -0
  159. package/templates/langs/zh/skills/pd-mentor/SKILL.md +230 -0
  160. package/templates/langs/zh/skills/plan-script/SKILL.md +32 -0
  161. package/templates/langs/zh/skills/profile/SKILL.md +24 -0
  162. package/templates/langs/zh/skills/reflection/SKILL.md +40 -0
  163. package/templates/langs/zh/skills/reflection-log/SKILL.md +37 -0
  164. package/templates/langs/zh/skills/report/SKILL.md +13 -0
  165. package/templates/langs/zh/skills/root-cause/SKILL.md +33 -0
  166. package/templates/langs/zh/skills/triage/SKILL.md +29 -0
  167. package/templates/langs/zh/skills/watch-evolution/SKILL.md +33 -0
  168. package/templates/pain_dictionary.json +36 -0
  169. package/templates/pain_settings.json +77 -0
  170. package/templates/workspace/.principles/00-kernel.md +51 -0
  171. package/templates/workspace/.principles/DECISION_POLICY.json +44 -0
  172. package/templates/workspace/.principles/PRINCIPLES.md +20 -0
  173. package/templates/workspace/.principles/PROFILE.json +52 -0
  174. package/templates/workspace/.principles/PROFILE.schema.json +56 -0
  175. package/templates/workspace/.principles/THINKING_OS.md +64 -0
  176. package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +7 -0
  177. package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +9 -0
  178. package/templates/workspace/.principles/models/_INDEX.md +27 -0
  179. package/templates/workspace/.principles/models/first_principles.md +62 -0
  180. package/templates/workspace/.principles/models/marketing_4p.md +52 -0
  181. package/templates/workspace/.principles/models/porter_five.md +63 -0
  182. package/templates/workspace/.principles/models/swot.md +60 -0
  183. package/templates/workspace/.principles/models/user_story_map.md +63 -0
  184. package/templates/workspace/.state/WORKBOARD.json +4 -0
  185. package/templates/workspace/AUDIT.md +15 -0
  186. package/templates/workspace/PLAN.md +2 -0
  187. package/templates/workspace/okr/RECOVERY_PROTOCOL.md +56 -0
  188. package/templates/workspace/okr/TASK_CHANGES.jsonl +6 -0
  189. 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
+ };