principles-disciple 1.5.4 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/commands/context.d.ts +5 -0
  2. package/dist/commands/context.js +312 -0
  3. package/dist/commands/evolution-status.d.ts +4 -0
  4. package/dist/commands/evolution-status.js +138 -0
  5. package/dist/commands/export.d.ts +2 -0
  6. package/dist/commands/export.js +45 -0
  7. package/dist/commands/focus.d.ts +14 -0
  8. package/dist/commands/focus.js +582 -0
  9. package/dist/commands/pain.js +143 -6
  10. package/dist/commands/principle-rollback.d.ts +4 -0
  11. package/dist/commands/principle-rollback.js +22 -0
  12. package/dist/commands/rollback.d.ts +19 -0
  13. package/dist/commands/rollback.js +119 -0
  14. package/dist/commands/samples.d.ts +2 -0
  15. package/dist/commands/samples.js +55 -0
  16. package/dist/core/config.d.ts +37 -0
  17. package/dist/core/config.js +47 -0
  18. package/dist/core/control-ui-db.d.ts +68 -0
  19. package/dist/core/control-ui-db.js +274 -0
  20. package/dist/core/detection-funnel.d.ts +1 -1
  21. package/dist/core/detection-funnel.js +4 -0
  22. package/dist/core/dictionary.d.ts +2 -0
  23. package/dist/core/dictionary.js +13 -0
  24. package/dist/core/event-log.d.ts +22 -1
  25. package/dist/core/event-log.js +319 -0
  26. package/dist/core/evolution-engine.d.ts +5 -5
  27. package/dist/core/evolution-engine.js +18 -18
  28. package/dist/core/evolution-migration.d.ts +5 -0
  29. package/dist/core/evolution-migration.js +65 -0
  30. package/dist/core/evolution-reducer.d.ts +69 -0
  31. package/dist/core/evolution-reducer.js +369 -0
  32. package/dist/core/evolution-types.d.ts +103 -0
  33. package/dist/core/focus-history.d.ts +65 -0
  34. package/dist/core/focus-history.js +266 -0
  35. package/dist/core/init.js +30 -7
  36. package/dist/core/migration.js +0 -2
  37. package/dist/core/path-resolver.d.ts +3 -0
  38. package/dist/core/path-resolver.js +90 -31
  39. package/dist/core/paths.d.ts +7 -8
  40. package/dist/core/paths.js +48 -40
  41. package/dist/core/profile.js +1 -1
  42. package/dist/core/session-tracker.d.ts +4 -0
  43. package/dist/core/session-tracker.js +15 -0
  44. package/dist/core/thinking-models.d.ts +38 -0
  45. package/dist/core/thinking-models.js +170 -0
  46. package/dist/core/trajectory.d.ts +184 -0
  47. package/dist/core/trajectory.js +817 -0
  48. package/dist/core/trust-engine.d.ts +2 -0
  49. package/dist/core/trust-engine.js +30 -4
  50. package/dist/core/workspace-context.d.ts +13 -0
  51. package/dist/core/workspace-context.js +50 -7
  52. package/dist/hooks/gate.js +301 -30
  53. package/dist/hooks/llm.d.ts +8 -0
  54. package/dist/hooks/llm.js +347 -69
  55. package/dist/hooks/message-sanitize.d.ts +3 -0
  56. package/dist/hooks/message-sanitize.js +37 -0
  57. package/dist/hooks/pain.js +105 -5
  58. package/dist/hooks/prompt.d.ts +20 -11
  59. package/dist/hooks/prompt.js +558 -158
  60. package/dist/hooks/subagent.d.ts +9 -2
  61. package/dist/hooks/subagent.js +40 -3
  62. package/dist/http/principles-console-route.d.ts +2 -0
  63. package/dist/http/principles-console-route.js +257 -0
  64. package/dist/i18n/commands.js +48 -20
  65. package/dist/index.js +264 -8
  66. package/dist/service/control-ui-query-service.d.ts +217 -0
  67. package/dist/service/control-ui-query-service.js +537 -0
  68. package/dist/service/empathy-observer-manager.d.ts +42 -0
  69. package/dist/service/empathy-observer-manager.js +147 -0
  70. package/dist/service/evolution-worker.d.ts +10 -0
  71. package/dist/service/evolution-worker.js +156 -24
  72. package/dist/service/trajectory-service.d.ts +2 -0
  73. package/dist/service/trajectory-service.js +15 -0
  74. package/dist/tools/agent-spawn.d.ts +27 -6
  75. package/dist/tools/agent-spawn.js +339 -87
  76. package/dist/tools/deep-reflect.d.ts +27 -7
  77. package/dist/tools/deep-reflect.js +282 -113
  78. package/dist/types/event-types.d.ts +84 -2
  79. package/dist/types/event-types.js +33 -0
  80. package/dist/types.d.ts +52 -0
  81. package/dist/types.js +24 -1
  82. package/openclaw.plugin.json +43 -11
  83. package/package.json +16 -6
  84. package/templates/langs/zh/core/HEARTBEAT.md +28 -4
  85. package/templates/langs/zh/skills/pd-daily/SKILL.md +97 -13
  86. package/templates/pain_settings.json +54 -2
  87. package/templates/workspace/.principles/PROFILE.json +2 -0
  88. package/templates/workspace/okr/CURRENT_FOCUS.md +57 -0
@@ -0,0 +1,266 @@
1
+ /**
2
+ * CURRENT_FOCUS 历史版本管理
3
+ *
4
+ * 功能:
5
+ * - 压缩时备份当前版本到历史目录
6
+ * - 清理过期历史版本
7
+ * - 读取历史版本(用于 full 模式)
8
+ */
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+ /**
12
+ * 简单的日志记录器
13
+ */
14
+ function logError(message, error) {
15
+ const timestamp = new Date().toISOString();
16
+ const errorStr = error instanceof Error ? error.message : String(error);
17
+ console.error(`[focus-history] ${timestamp} ERROR: ${message}${errorStr ? ' - ' + errorStr : ''}`);
18
+ }
19
+ /** 历史版本保留数量 */
20
+ const MAX_HISTORY_FILES = 10;
21
+ /** full 模式读取的历史版本数 */
22
+ const FULL_MODE_HISTORY_COUNT = 3;
23
+ /**
24
+ * 获取历史目录路径
25
+ */
26
+ export function getHistoryDir(focusPath) {
27
+ return path.join(path.dirname(focusPath), '.history');
28
+ }
29
+ /**
30
+ * 从 CURRENT_FOCUS.md 提取版本号
31
+ * 支持整数和小数版本(如 v1, v1.1, v1.2)
32
+ */
33
+ export function extractVersion(content) {
34
+ const match = content.match(/\*\*版本\*\*:\s*v([\d.]+)/i);
35
+ return match ? match[1] : '1';
36
+ }
37
+ /**
38
+ * 从 CURRENT_FOCUS.md 提取更新日期
39
+ */
40
+ export function extractDate(content) {
41
+ const match = content.match(/\*\*更新\*\*:\s*(\d{4}-\d{2}-\d{2})/);
42
+ return match ? match[1] : new Date().toISOString().split('T')[0];
43
+ }
44
+ /**
45
+ * 备份当前版本到历史目录
46
+ *
47
+ * @param focusPath CURRENT_FOCUS.md 的完整路径
48
+ * @param content 当前内容
49
+ * @returns 备份文件路径,失败返回 null
50
+ */
51
+ export function backupToHistory(focusPath, content) {
52
+ try {
53
+ const historyDir = getHistoryDir(focusPath);
54
+ // 确保历史目录存在
55
+ if (!fs.existsSync(historyDir)) {
56
+ try {
57
+ fs.mkdirSync(historyDir, { recursive: true });
58
+ }
59
+ catch (error) {
60
+ logError(`Failed to create history directory: ${historyDir}`, error);
61
+ return null;
62
+ }
63
+ }
64
+ const version = extractVersion(content);
65
+ const date = extractDate(content);
66
+ // 使用时间戳作为唯一标识,避免同名冲突
67
+ const timestamp = Date.now();
68
+ const backupName = `CURRENT_FOCUS.v${version}.${date}.${timestamp}.md`;
69
+ const backupPath = path.join(historyDir, backupName);
70
+ // 如果备份已存在,跳过
71
+ if (fs.existsSync(backupPath)) {
72
+ return null;
73
+ }
74
+ try {
75
+ fs.writeFileSync(backupPath, content, 'utf-8');
76
+ return backupPath;
77
+ }
78
+ catch (error) {
79
+ logError(`Failed to write backup file: ${backupPath}`, error);
80
+ return null;
81
+ }
82
+ }
83
+ catch (error) {
84
+ logError('Unexpected error in backupToHistory', error);
85
+ return null;
86
+ }
87
+ }
88
+ /**
89
+ * 清理过期历史版本
90
+ *
91
+ * @param focusPath CURRENT_FOCUS.md 的完整路径
92
+ * @param maxFiles 最大保留数量
93
+ */
94
+ export function cleanupHistory(focusPath, maxFiles = MAX_HISTORY_FILES) {
95
+ try {
96
+ const historyDir = getHistoryDir(focusPath);
97
+ if (!fs.existsSync(historyDir)) {
98
+ return;
99
+ }
100
+ // 获取所有历史文件并按修改时间排序(最新的在前)
101
+ const files = fs.readdirSync(historyDir)
102
+ .filter(f => f.startsWith('CURRENT_FOCUS.v') && f.endsWith('.md'))
103
+ .map(f => ({
104
+ name: f,
105
+ path: path.join(historyDir, f),
106
+ mtime: fs.statSync(path.join(historyDir, f)).mtime.getTime()
107
+ }))
108
+ .sort((a, b) => b.mtime - a.mtime);
109
+ // 删除超出数量的文件
110
+ const toDelete = files.slice(maxFiles);
111
+ for (const file of toDelete) {
112
+ try {
113
+ fs.unlinkSync(file.path);
114
+ }
115
+ catch (error) {
116
+ // 单个文件删除失败不应中断整个清理过程
117
+ logError(`Failed to delete history file: ${file.path}`, error);
118
+ }
119
+ }
120
+ }
121
+ catch (error) {
122
+ logError('Unexpected error in cleanupHistory', error);
123
+ }
124
+ }
125
+ /**
126
+ * 获取历史版本列表
127
+ *
128
+ * @param focusPath CURRENT_FOCUS.md 的完整路径
129
+ * @param count 获取数量
130
+ * @returns 历史版本内容数组(按时间倒序)
131
+ */
132
+ export function getHistoryVersions(focusPath, count = FULL_MODE_HISTORY_COUNT) {
133
+ const historyDir = getHistoryDir(focusPath);
134
+ if (!fs.existsSync(historyDir)) {
135
+ return [];
136
+ }
137
+ // 获取所有历史文件并按修改时间排序(最新的在前)
138
+ const files = fs.readdirSync(historyDir)
139
+ .filter(f => f.startsWith('CURRENT_FOCUS.v') && f.endsWith('.md'))
140
+ .map(f => ({
141
+ path: path.join(historyDir, f),
142
+ mtime: fs.statSync(path.join(historyDir, f)).mtime.getTime()
143
+ }))
144
+ .sort((a, b) => b.mtime - a.mtime)
145
+ .slice(0, count);
146
+ return files.map(f => fs.readFileSync(f.path, 'utf-8'));
147
+ }
148
+ /**
149
+ * 压缩 CURRENT_FOCUS.md
150
+ *
151
+ * @param focusPath CURRENT_FOCUS.md 的完整路径
152
+ * @param newContent 新内容
153
+ * @returns 压缩后的信息
154
+ */
155
+ export function compressFocus(focusPath, newContent) {
156
+ // 读取当前内容
157
+ let oldContent = '';
158
+ if (fs.existsSync(focusPath)) {
159
+ oldContent = fs.readFileSync(focusPath, 'utf-8');
160
+ }
161
+ // 备份当前版本
162
+ const backupPath = oldContent ? backupToHistory(focusPath, oldContent) : null;
163
+ // 递增版本号(支持小数版本)
164
+ const oldVersion = extractVersion(oldContent);
165
+ // 解析版本号并递增
166
+ const versionParts = oldVersion.split('.');
167
+ const majorVersion = parseInt(versionParts[0], 10) || 1;
168
+ const newVersion = `${majorVersion + 1}`;
169
+ const today = new Date().toISOString().split('T')[0];
170
+ // 更新版本号和日期
171
+ const updatedContent = newContent
172
+ .replace(/\*\*版本\*\*:\s*v[\d.]+/i, `**版本**: v${newVersion}`)
173
+ .replace(/\*\*更新\*\*:\s*\d{4}-\d{2}-\d{2}/, `**更新**: ${today}`);
174
+ // 写入新内容
175
+ fs.writeFileSync(focusPath, updatedContent, 'utf-8');
176
+ // 清理过期历史
177
+ const historyDir = getHistoryDir(focusPath);
178
+ const beforeCount = fs.existsSync(historyDir)
179
+ ? fs.readdirSync(historyDir).filter(f => f.startsWith('CURRENT_FOCUS.v')).length
180
+ : 0;
181
+ cleanupHistory(focusPath);
182
+ const afterCount = fs.existsSync(historyDir)
183
+ ? fs.readdirSync(historyDir).filter(f => f.startsWith('CURRENT_FOCUS.v')).length
184
+ : 0;
185
+ return {
186
+ backupPath,
187
+ cleanedCount: beforeCount - afterCount
188
+ };
189
+ }
190
+ /**
191
+ * 智能摘要提取
192
+ *
193
+ * 优先提取关键章节,确保不丢失重要信息
194
+ * 对于非结构化内容,回退到简单的行截取
195
+ *
196
+ * @param content CURRENT_FOCUS.md 内容
197
+ * @param maxLines 最大行数
198
+ */
199
+ export function extractSummary(content, maxLines = 30) {
200
+ const lines = content.split('\n');
201
+ const sections = {
202
+ header: [], // 标题和元数据
203
+ snapshot: [], // 状态快照
204
+ current: [], // 当前任务
205
+ nextSteps: [], // 下一步
206
+ reference: [] // 参考
207
+ };
208
+ let currentSection = 'header';
209
+ let hasStructuredSections = false;
210
+ for (const line of lines) {
211
+ // 识别章节(使用更宽松的匹配,支持不同格式)
212
+ const trimmedLine = line.trim();
213
+ // 使用正则匹配,支持 h1-h3 和多种格式
214
+ if (/^#{1,3}\s*.*状态快照|📍/.test(trimmedLine)) {
215
+ currentSection = 'snapshot';
216
+ hasStructuredSections = true;
217
+ }
218
+ else if (/^#{1,3}\s*.*当前任务|🔄/.test(trimmedLine)) {
219
+ currentSection = 'current';
220
+ hasStructuredSections = true;
221
+ }
222
+ else if (/^#{1,3}\s*.*下一步|➡️/.test(trimmedLine)) {
223
+ currentSection = 'nextSteps';
224
+ hasStructuredSections = true;
225
+ }
226
+ else if (/^#{1,3}\s*.*参考|📎/.test(trimmedLine)) {
227
+ currentSection = 'reference';
228
+ hasStructuredSections = true;
229
+ }
230
+ else if (trimmedLine === '---') {
231
+ continue; // 跳过分隔线
232
+ }
233
+ else if (line.startsWith('<!--')) {
234
+ continue; // 跳过注释
235
+ }
236
+ sections[currentSection].push(line);
237
+ }
238
+ // 如果没有结构化章节,回退到简单的行截取
239
+ if (!hasStructuredSections) {
240
+ const result = lines.slice(0, maxLines);
241
+ if (lines.length > maxLines) {
242
+ result.push('');
243
+ result.push('...[truncated, see CURRENT_FOCUS.md for full context]');
244
+ }
245
+ return result.join('\n');
246
+ }
247
+ // 按优先级拼接
248
+ const result = [
249
+ ...sections.header.slice(0, 5), // 标题 + 元数据
250
+ '',
251
+ '---',
252
+ '',
253
+ ...sections.snapshot.slice(0, 10), // 状态快照
254
+ '',
255
+ ...sections.nextSteps.slice(0, 10), // 下一步(优先级高)
256
+ '',
257
+ ...sections.current.slice(0, 15), // 当前任务
258
+ ];
259
+ // 限制总行数
260
+ const trimmed = result.slice(0, maxLines);
261
+ if (result.length > maxLines) {
262
+ trimmed.push('');
263
+ trimmed.push('...[truncated, see CURRENT_FOCUS.md for full context]');
264
+ }
265
+ return trimmed.join('\n');
266
+ }
package/dist/core/init.js CHANGED
@@ -2,6 +2,15 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { PD_DIRS } from './paths.js';
5
+ import { defaultContextConfig } from '../types.js';
6
+ /**
7
+ * Default PROFILE.json content
8
+ */
9
+ const DEFAULT_PROFILE = {
10
+ name: "Principles Disciple Agent",
11
+ version: "1.0.0",
12
+ contextInjection: defaultContextConfig
13
+ };
5
14
  /**
6
15
  * Ensures that the workspace has the necessary template files for Principles Disciple.
7
16
  * This function flattens 'core' templates to the root so OpenClaw can find them.
@@ -47,6 +56,17 @@ export function ensureWorkspaceTemplates(api, workspaceDir, language = 'en') {
47
56
  }
48
57
  copyRecursiveSync(painTemplatesDir, painDestDir, api);
49
58
  }
59
+ // 4. Initialize PROFILE.json with default contextInjection config
60
+ const principlesDir = path.join(workspaceDir, PD_DIRS.IDENTITY);
61
+ const profilePath = path.join(principlesDir, 'PROFILE.json');
62
+ if (!fs.existsSync(profilePath)) {
63
+ // Ensure .principles directory exists
64
+ if (!fs.existsSync(principlesDir)) {
65
+ fs.mkdirSync(principlesDir, { recursive: true });
66
+ }
67
+ fs.writeFileSync(profilePath, JSON.stringify(DEFAULT_PROFILE, null, 2), 'utf-8');
68
+ api.logger.info(`[PD] Initialized PROFILE.json with default contextInjection config`);
69
+ }
50
70
  }
51
71
  catch (err) {
52
72
  api.logger.error(`[PD] Failed to initialize workspace templates: ${String(err)}`);
@@ -54,12 +74,15 @@ export function ensureWorkspaceTemplates(api, workspaceDir, language = 'en') {
54
74
  }
55
75
  /**
56
76
  * Standard recursive copy that preserves directory structure.
77
+ * Special handling: maps 'okr' directory to 'memory/okr' for runtime compatibility.
57
78
  */
58
79
  function copyRecursiveSync(srcDir, destDir, api) {
59
80
  const items = fs.readdirSync(srcDir);
60
81
  for (const item of items) {
61
82
  const srcPath = path.join(srcDir, item);
62
- const destPath = path.join(destDir, item);
83
+ // Special mapping: okr -> memory/okr (runtime path expects memory/okr/)
84
+ const destItemName = item === 'okr' ? path.join('memory', 'okr') : item;
85
+ const destPath = path.join(destDir, destItemName);
63
86
  const stat = fs.statSync(srcPath);
64
87
  if (stat.isDirectory()) {
65
88
  if (!fs.existsSync(destPath)) {
@@ -69,13 +92,13 @@ function copyRecursiveSync(srcDir, destDir, api) {
69
92
  }
70
93
  else {
71
94
  if (!fs.existsSync(destPath)) {
72
- try {
73
- fs.copyFileSync(srcPath, destPath);
74
- }
75
- catch (err) {
76
- if ('logger' in api)
77
- api.logger.warn(`[PD] Failed to copy ${item}: ${String(err)}`);
95
+ // Ensure parent directory exists
96
+ const destParent = path.dirname(destPath);
97
+ if (!fs.existsSync(destParent)) {
98
+ fs.mkdirSync(destParent, { recursive: true });
78
99
  }
100
+ fs.copyFileSync(srcPath, destPath);
101
+ api.logger.info(`[PD] Copied: ${destItemName}`);
79
102
  }
80
103
  }
81
104
  }
@@ -12,14 +12,12 @@ export function migrateDirectoryStructure(api, workspaceDir) {
12
12
  // Comprehensive migration map covering ALL legacy locations
13
13
  const migrationMap = [
14
14
  // From docs/
15
- { legacy: path.join(legacyDocsDir, 'PROFILE.json'), newKey: 'PROFILE' },
16
15
  { legacy: path.join(legacyDocsDir, 'PRINCIPLES.md'), newKey: 'PRINCIPLES' },
17
16
  { legacy: path.join(legacyDocsDir, 'THINKING_OS.md'), newKey: 'THINKING_OS' },
18
17
  { legacy: path.join(legacyDocsDir, '00-kernel.md'), newKey: 'KERNEL' },
19
18
  { legacy: path.join(legacyDocsDir, 'DECISION_POLICY.json'), newKey: 'DECISION_POLICY' },
20
19
  { legacy: path.join(legacyDocsDir, 'PLAN.md'), newKey: 'PLAN' },
21
20
  { legacy: path.join(legacyDocsDir, 'evolution_queue.json'), newKey: 'EVOLUTION_QUEUE' },
22
- { legacy: path.join(legacyDocsDir, 'AGENT_SCORECARD.json'), newKey: 'AGENT_SCORECARD' },
23
21
  { legacy: path.join(legacyDocsDir, '.pain_flag'), newKey: 'PAIN_FLAG' },
24
22
  { legacy: path.join(legacyDocsDir, 'SYSTEM_CAPABILITIES.json'), newKey: 'SYSTEM_CAPABILITIES' },
25
23
  { legacy: path.join(legacyDocsDir, 'SYSTEM.log'), newKey: 'SYSTEM_LOG' },
@@ -24,11 +24,14 @@ export interface PDConfig {
24
24
  debug?: boolean;
25
25
  }
26
26
  export declare class PathResolver {
27
+ private static extensionRoot;
27
28
  private workspaceDir;
28
29
  private stateDir;
29
30
  private readonly logger?;
30
31
  private readonly normalizeWorkspace;
31
32
  private initialized;
33
+ static setExtensionRoot(extensionRootPath: string): void;
34
+ static getExtensionRoot(): string | null;
32
35
  constructor(options?: PathResolverOptions);
33
36
  private log;
34
37
  private detectWorkspaceDir;
@@ -42,6 +42,21 @@ const PD_CONFIG_LOCATIONS = [
42
42
  path.join(os.homedir(), '.openclaw', PD_CONFIG_FILE),
43
43
  path.join(os.homedir(), '.principles', PD_CONFIG_FILE),
44
44
  ];
45
+ function isWindowsPath(inputPath) {
46
+ return /^[A-Za-z]:[\\/]/.test(inputPath) || inputPath.startsWith('\\\\');
47
+ }
48
+ function isPosixAbsolutePath(inputPath) {
49
+ return inputPath.startsWith('/');
50
+ }
51
+ function getPathApi(inputPath) {
52
+ if (isWindowsPath(inputPath)) {
53
+ return path.win32;
54
+ }
55
+ if (isPosixAbsolutePath(inputPath)) {
56
+ return path.posix;
57
+ }
58
+ return path;
59
+ }
45
60
  function findConfigFile() {
46
61
  for (const loc of PD_CONFIG_LOCATIONS) {
47
62
  if (fs.existsSync(loc)) {
@@ -67,16 +82,34 @@ function loadConfigFromFile() {
67
82
  }
68
83
  }
69
84
  export class PathResolver {
85
+ static extensionRoot = null;
70
86
  workspaceDir = null;
71
87
  stateDir = null;
72
88
  logger;
73
89
  normalizeWorkspace;
74
90
  initialized = false;
91
+ static setExtensionRoot(extensionRootPath) {
92
+ if (!extensionRootPath || !extensionRootPath.trim()) {
93
+ return;
94
+ }
95
+ const trimmed = extensionRootPath.trim();
96
+ const pathApi = getPathApi(trimmed);
97
+ PathResolver.extensionRoot = pathApi.normalize(trimmed);
98
+ }
99
+ static getExtensionRoot() {
100
+ return PathResolver.extensionRoot;
101
+ }
75
102
  constructor(options = {}) {
76
103
  this.logger = options.logger;
77
104
  this.normalizeWorkspace = options.normalizeWorkspace ?? true;
78
105
  if (options.workspaceDir) {
79
- this.workspaceDir = options.workspaceDir;
106
+ const original = options.workspaceDir;
107
+ const normalized = this.normalizeWorkspace ? this.normalizePath(original) : original;
108
+ if (original !== normalized) {
109
+ this.log('info', `Workspace path normalized: ${original} -> ${normalized}`);
110
+ }
111
+ this.workspaceDir = normalized;
112
+ this.initialized = true;
80
113
  }
81
114
  }
82
115
  log(level, msg) {
@@ -122,12 +155,13 @@ export class PathResolver {
122
155
  return defaultWorkspace;
123
156
  }
124
157
  normalizePath(inputPath) {
125
- let normalized = path.resolve(inputPath);
158
+ const pathApi = getPathApi(inputPath);
159
+ let normalized = pathApi === path ? path.resolve(inputPath) : pathApi.normalize(inputPath);
126
160
  if (this.normalizeWorkspace) {
127
- const problematicSuffixes = ['/memory', '/docs'];
161
+ const problematicSuffixes = ['/memory', '/docs', '\\memory', '\\docs'];
128
162
  for (const suffix of problematicSuffixes) {
129
163
  if (normalized.endsWith(suffix)) {
130
- const parent = path.dirname(normalized);
164
+ const parent = pathApi.dirname(normalized);
131
165
  this.log('warn', `Detected subdirectory suffix '${suffix}' in path. Normalized to parent: ${parent}`);
132
166
  normalized = parent;
133
167
  break;
@@ -166,47 +200,72 @@ export class PathResolver {
166
200
  this.log('info', `Using state directory from PD_STATE_DIR: ${this.stateDir}`);
167
201
  return this.stateDir;
168
202
  }
203
+ // If workspaceDir was explicitly provided via constructor, use workspace-based state dir
204
+ // This ensures tests and programmatic usage don't get polluted by global config
205
+ if (this.initialized && this.workspaceDir) {
206
+ const pathApi = getPathApi(this.workspaceDir);
207
+ this.stateDir = pathApi.join(this.workspaceDir, '.state');
208
+ this.log('debug', `Using workspace-based state directory: ${this.stateDir}`);
209
+ return this.stateDir;
210
+ }
169
211
  const fileConfig = loadConfigFromFile();
170
212
  if (fileConfig?.state) {
171
213
  this.stateDir = fileConfig.state;
172
214
  this.log('info', `Using state directory from config file: ${this.stateDir}`);
173
215
  return this.stateDir;
174
216
  }
175
- this.stateDir = path.join(this.getWorkspaceDir(), '.state');
217
+ const workspaceDir = this.getWorkspaceDir();
218
+ const pathApi = getPathApi(workspaceDir);
219
+ this.stateDir = pathApi.join(workspaceDir, '.state');
176
220
  this.log('debug', `Computed state directory: ${this.stateDir}`);
177
221
  return this.stateDir;
178
222
  }
179
223
  resolve(key) {
180
224
  const workspace = this.getWorkspaceDir();
181
225
  const state = this.getStateDir();
182
- const memory = path.join(workspace, 'memory');
226
+ const workspacePath = getPathApi(workspace);
227
+ const extensionRoot = PathResolver.extensionRoot || path.resolve(process.cwd(), 'packages', 'openclaw-plugin');
228
+ const extensionPath = getPathApi(extensionRoot);
229
+ const memory = workspacePath.join(workspace, 'memory');
230
+ const extensionSrc = extensionPath.join(extensionRoot, 'src');
231
+ const extensionDist = extensionPath.join(extensionRoot, 'dist');
232
+ const evolutionWorker = fs.existsSync(extensionSrc)
233
+ ? extensionPath.join(extensionSrc, 'service', 'evolution-worker.ts')
234
+ : extensionPath.join(extensionDist, 'service', 'evolution-worker.js');
183
235
  const pathMap = {
184
- 'PROFILE': path.join(workspace, '.principles', 'PROFILE.json'),
185
- 'PRINCIPLES': path.join(workspace, '.principles', 'PRINCIPLES.md'),
186
- 'THINKING_OS': path.join(workspace, '.principles', 'THINKING_OS.md'),
187
- 'KERNEL': path.join(workspace, '.principles', '00-kernel.md'),
188
- 'DECISION_POLICY': path.join(workspace, '.principles', 'DECISION_POLICY.json'),
189
- 'MODELS_DIR': path.join(workspace, '.principles', 'models'),
190
- 'PLAN': path.join(workspace, 'PLAN.md'),
191
- 'AGENT_SCORECARD': path.join(state, 'AGENT_SCORECARD.json'),
192
- 'PAIN_FLAG': path.join(state, '.pain_flag'),
193
- 'EVOLUTION_QUEUE': path.join(state, 'evolution_queue.json'),
194
- 'EVOLUTION_DIRECTIVE': path.join(state, 'evolution_directive.json'),
195
- 'WORKBOARD': path.join(state, 'WORKBOARD.json'),
196
- 'SYSTEM_CAPABILITIES': path.join(state, 'SYSTEM_CAPABILITIES.json'),
197
- 'PAIN_SETTINGS': path.join(state, 'pain_settings.json'),
198
- 'PAIN_CANDIDATES': path.join(state, 'pain_candidates.json'),
199
- 'THINKING_OS_USAGE': path.join(state, 'thinking_os_usage.json'),
200
- 'DICTIONARY': path.join(state, 'pain_dictionary.json'),
236
+ 'PROFILE': workspacePath.join(workspace, '.principles', 'PROFILE.json'),
237
+ 'PRINCIPLES': workspacePath.join(workspace, '.principles', 'PRINCIPLES.md'),
238
+ 'THINKING_OS': workspacePath.join(workspace, '.principles', 'THINKING_OS.md'),
239
+ 'KERNEL': workspacePath.join(workspace, '.principles', '00-kernel.md'),
240
+ 'DECISION_POLICY': workspacePath.join(workspace, '.principles', 'DECISION_POLICY.json'),
241
+ 'MODELS_DIR': workspacePath.join(workspace, '.principles', 'models'),
242
+ 'PLAN': workspacePath.join(workspace, 'PLAN.md'),
243
+ 'AGENT_SCORECARD': workspacePath.join(state, 'AGENT_SCORECARD.json'),
244
+ 'PAIN_FLAG': workspacePath.join(state, '.pain_flag'),
245
+ 'EVOLUTION_QUEUE': workspacePath.join(state, 'evolution_queue.json'),
246
+ 'EVOLUTION_DIRECTIVE': workspacePath.join(state, 'evolution_directive.json'),
247
+ 'WORKBOARD': workspacePath.join(state, 'WORKBOARD.json'),
248
+ 'SYSTEM_CAPABILITIES': workspacePath.join(state, 'SYSTEM_CAPABILITIES.json'),
249
+ 'PAIN_SETTINGS': workspacePath.join(state, 'pain_settings.json'),
250
+ 'PAIN_CANDIDATES': workspacePath.join(state, 'pain_candidates.json'),
251
+ 'THINKING_OS_USAGE': workspacePath.join(state, 'thinking_os_usage.json'),
252
+ 'DICTIONARY': workspacePath.join(state, 'pain_dictionary.json'),
201
253
  'STATE_DIR': state,
202
- 'LOGS': path.join(memory, 'logs'),
203
- 'SYSTEM_LOG': path.join(memory, 'logs', 'SYSTEM.log'),
204
- 'REFLECTION_LOG': path.join(memory, 'reflection-log.md'),
205
- 'USER_CONTEXT': path.join(memory, 'USER_CONTEXT.md'),
206
- 'OKR_DIR': path.join(memory, 'okr'),
207
- 'CURRENT_FOCUS': path.join(memory, 'okr', 'CURRENT_FOCUS.md'),
208
- 'WEEK_STATE': path.join(memory, 'okr', 'WEEK_STATE.json'),
209
- 'THINKING_OS_CANDIDATES': path.join(memory, 'THINKING_OS_CANDIDATES.md'),
254
+ 'EXTENSION_ROOT': extensionRoot,
255
+ 'EXTENSION_SRC': extensionSrc,
256
+ 'EXTENSION_DIST': extensionDist,
257
+ 'EVOLUTION_WORKER': evolutionWorker,
258
+ 'LOGS': workspacePath.join(memory, 'logs'),
259
+ 'SYSTEM_LOG': workspacePath.join(memory, 'logs', 'SYSTEM.log'),
260
+ 'REFLECTION_LOG': workspacePath.join(memory, 'reflection-log.md'),
261
+ 'USER_CONTEXT': workspacePath.join(memory, 'USER_CONTEXT.md'),
262
+ 'OKR_DIR': workspacePath.join(memory, 'okr'),
263
+ 'CURRENT_FOCUS': workspacePath.join(memory, 'okr', 'CURRENT_FOCUS.md'),
264
+ 'WEEK_STATE': workspacePath.join(memory, 'okr', 'WEEK_STATE.json'),
265
+ 'THINKING_OS_CANDIDATES': workspacePath.join(memory, 'THINKING_OS_CANDIDATES.md'),
266
+ 'EVOLUTION_STREAM': workspacePath.join(memory, 'evolution.jsonl'),
267
+ 'EVOLUTION_LOCK': workspacePath.join(memory, '.locks', 'evolution'),
268
+ 'PRINCIPLE_BLACKLIST': workspacePath.join(state, 'principle_blacklist.json'),
210
269
  'MEMORY': memory,
211
270
  };
212
271
  const resolved = pathMap[key];
@@ -3,22 +3,15 @@
3
3
  * Establishing a logical separation between Identity, State, and Memory.
4
4
  */
5
5
  export declare const PD_DIRS: {
6
- /** 🧬 Core configuration, identity, and kernel rules (hidden) */
7
6
  IDENTITY: string;
8
- /** 🧠 Deep Reflection mental models */
9
7
  MODELS: string;
10
- /** ⚡ Volatile operational data, queues, and task status (hidden) */
11
8
  STATE: string;
12
- /** 💾 Historical records, logs, and long-term memory */
13
9
  MEMORY: string;
14
- /** 🎯 Strategic objectives and focus areas */
15
10
  OKR: string;
16
- /** 📁 Internal logs directory */
17
11
  LOGS: string;
18
- /** 🧠 Session persistence directory */
19
12
  SESSIONS: string;
20
- /** 🩹 Semantic pain samples for L3 retrieval */
21
13
  PAIN_SAMPLES: string;
14
+ LOCKS: string;
22
15
  };
23
16
  /**
24
17
  * Standard File Path Mappings
@@ -40,8 +33,12 @@ export declare const PD_FILES: {
40
33
  PAIN_SETTINGS: string;
41
34
  PAIN_CANDIDATES: string;
42
35
  THINKING_OS_USAGE: string;
36
+ TRAJECTORY_DB: string;
37
+ TRAJECTORY_BLOBS_DIR: string;
38
+ EXPORTS_DIR: string;
43
39
  SESSION_DIR: string;
44
40
  DICTIONARY: string;
41
+ PRINCIPLE_BLACKLIST: string;
45
42
  PLAN: string;
46
43
  MEMORY_MD: string;
47
44
  HEARTBEAT: string;
@@ -53,6 +50,8 @@ export declare const PD_FILES: {
53
50
  WEEK_STATE: string;
54
51
  THINKING_OS_CANDIDATES: string;
55
52
  SEMANTIC_PAIN: string;
53
+ EVOLUTION_STREAM: string;
54
+ EVOLUTION_LOCK: string;
56
55
  };
57
56
  /**
58
57
  * Resolves a PD file path within a given workspace.