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,284 @@
1
+ /**
2
+ * Trust Engine V2.1 - Recalibrated for Action Classification
3
+ * Differentiates between Exploratory (safe) and Constructive (risky) actions.
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import { EventLogService } from './event-log.js';
8
+ import { resolvePdPath } from './paths.js';
9
+ import { ConfigService } from './config-service.js';
10
+ export const EXPLORATORY_TOOLS = [
11
+ // 文件读取
12
+ 'read', 'read_file', 'read_many_files', 'image_read',
13
+ // 搜索和列表
14
+ 'search_file_content', 'grep', 'grep_search', 'list_directory', 'ls', 'glob',
15
+ // Web
16
+ 'web_fetch', 'web_search',
17
+ // 用户交互
18
+ 'ask_user', 'ask_user_question',
19
+ // LSP
20
+ 'lsp_hover', 'lsp_goto_definition', 'lsp_find_references',
21
+ // 内存和状态
22
+ 'memory_recall', 'save_memory', 'todo_read', 'todo_write',
23
+ // 状态查询
24
+ 'pd-status', 'trust', 'report',
25
+ ];
26
+ export const CONSTRUCTIVE_TOOLS = [
27
+ 'write', 'write_file', 'edit', 'edit_file', 'replace', 'apply_patch',
28
+ 'insert', 'patch', 'delete_file', 'move_file', 'run_shell_command',
29
+ 'pd_spawn_agent', 'sessions_spawn', 'evolve-task', 'init-strategy'
30
+ ];
31
+ export class TrustEngine {
32
+ scorecard;
33
+ workspaceDir;
34
+ stateDir;
35
+ constructor(workspaceDir) {
36
+ this.workspaceDir = workspaceDir;
37
+ this.stateDir = resolvePdPath(workspaceDir, 'STATE_DIR');
38
+ this.scorecard = this.loadScorecard();
39
+ const scorecardPath = resolvePdPath(this.workspaceDir, 'AGENT_SCORECARD');
40
+ if (!fs.existsSync(scorecardPath)) {
41
+ this.saveScorecard();
42
+ }
43
+ }
44
+ get config() {
45
+ return ConfigService.get(this.stateDir);
46
+ }
47
+ get trustSettings() {
48
+ const settings = this.config.get('trust');
49
+ return settings || {
50
+ stages: { stage_1_observer: 30, stage_2_editor: 60, stage_3_developer: 80 },
51
+ cold_start: { initial_trust: 85, grace_failures: 5, cold_start_period_ms: 86400000 },
52
+ penalties: { tool_failure_base: -2, risky_failure_base: -10, gate_bypass_attempt: -5, failure_streak_multiplier: -2, max_penalty: -20 },
53
+ rewards: { success_base: 2, subagent_success: 5, tool_success_reward: 0.2, streak_bonus_threshold: 3, streak_bonus: 5, recovery_boost: 5, max_reward: 15 },
54
+ limits: { stage_2_max_lines: 50, stage_3_max_lines: 300 }
55
+ };
56
+ }
57
+ loadScorecard() {
58
+ const scorecardPath = resolvePdPath(this.workspaceDir, 'AGENT_SCORECARD');
59
+ const settings = this.trustSettings;
60
+ if (fs.existsSync(scorecardPath)) {
61
+ try {
62
+ const raw = fs.readFileSync(scorecardPath, 'utf8');
63
+ const data = JSON.parse(raw);
64
+ if (data.score !== undefined && data.trust_score === undefined)
65
+ data.trust_score = data.score;
66
+ if (!data.history)
67
+ data.history = [];
68
+ if (data.exploratory_failure_streak === undefined)
69
+ data.exploratory_failure_streak = 0;
70
+ return data;
71
+ }
72
+ catch (e) {
73
+ console.error(`[PD:TrustEngine] FATAL: Failed to parse scorecard at ${scorecardPath}. Resetting.`);
74
+ }
75
+ }
76
+ const now = new Date();
77
+ const coldStartEnd = new Date(now.getTime() + settings.cold_start.cold_start_period_ms);
78
+ return {
79
+ trust_score: settings.cold_start.initial_trust,
80
+ success_streak: 0,
81
+ failure_streak: 0,
82
+ exploratory_failure_streak: 0,
83
+ grace_failures_remaining: settings.cold_start.grace_failures,
84
+ last_updated: now.toISOString(),
85
+ cold_start_end: coldStartEnd.toISOString(),
86
+ first_activity_at: now.toISOString(),
87
+ history: []
88
+ };
89
+ }
90
+ saveScorecard() {
91
+ const scorecardPath = resolvePdPath(this.workspaceDir, 'AGENT_SCORECARD');
92
+ try {
93
+ const dir = path.dirname(scorecardPath);
94
+ if (!fs.existsSync(dir))
95
+ fs.mkdirSync(dir, { recursive: true });
96
+ fs.writeFileSync(scorecardPath, JSON.stringify(this.scorecard, null, 2), 'utf8');
97
+ }
98
+ catch (e) {
99
+ console.error(`[PD:TrustEngine] Failed to save scorecard: ${String(e)}`);
100
+ }
101
+ }
102
+ getScore() { return this.scorecard.trust_score; }
103
+ getScorecard() { return this.scorecard; }
104
+ getStage() {
105
+ const score = this.scorecard.trust_score;
106
+ const stages = this.trustSettings.stages;
107
+ if (score < stages.stage_1_observer)
108
+ return 1;
109
+ if (score < stages.stage_2_editor)
110
+ return 2;
111
+ if (score < stages.stage_3_developer)
112
+ return 3;
113
+ return 4;
114
+ }
115
+ isColdStart() {
116
+ if (!this.scorecard.cold_start_end)
117
+ return false;
118
+ return new Date() < new Date(this.scorecard.cold_start_end);
119
+ }
120
+ recordSuccess(reason, context, isSubagent = false) {
121
+ const settings = this.trustSettings;
122
+ const rewards = settings.rewards;
123
+ const toolName = context?.toolName;
124
+ // 1. Check if this is an exploratory tool success
125
+ const isExploratory = toolName && EXPLORATORY_TOOLS.includes(toolName);
126
+ if (reason === 'tool_success' && isExploratory) {
127
+ // Exploratory tools don't grant trust points, but they:
128
+ // 1. Reset the exploratory failure streak
129
+ // 2. Prove the agent isn't stuck (no delta, no success_streak increment)
130
+ this.scorecard.exploratory_failure_streak = 0;
131
+ this.updateScore(0, `Exploratory Success: ${toolName}`, 'info', context);
132
+ return;
133
+ }
134
+ let delta = rewards.success_base;
135
+ if (isSubagent) {
136
+ delta = rewards.subagent_success;
137
+ }
138
+ else if (reason === 'tool_success') {
139
+ delta = rewards.tool_success_reward ?? 0.2;
140
+ }
141
+ else if (reason === 'plan_ready') {
142
+ delta = 5; // Strategic reward
143
+ }
144
+ this.scorecard.success_streak++;
145
+ this.scorecard.failure_streak = 0; // Reset failure streak on constructive success
146
+ this.scorecard.exploratory_failure_streak = 0;
147
+ if (this.scorecard.success_streak >= rewards.streak_bonus_threshold) {
148
+ delta += rewards.streak_bonus;
149
+ }
150
+ if (this.scorecard.trust_score < settings.stages.stage_1_observer) {
151
+ delta += rewards.recovery_boost;
152
+ }
153
+ this.updateScore(delta, reason, 'success', context);
154
+ }
155
+ recordFailure(type, context) {
156
+ const settings = this.trustSettings;
157
+ const penalties = settings.penalties;
158
+ const toolName = context?.toolName;
159
+ // 1. Classification: Is this an exploratory failure?
160
+ const isExploratory = toolName && EXPLORATORY_TOOLS.includes(toolName);
161
+ // 2. Cold start grace (only for non-risky actions)
162
+ if (type !== 'risky' && this.isColdStart() && (this.scorecard.grace_failures_remaining || 0) > 0) {
163
+ this.scorecard.grace_failures_remaining = (this.scorecard.grace_failures_remaining || 0) - 1;
164
+ this.updateScore(0, `Grace Failure consumed (${toolName || type})`, 'failure', context);
165
+ return;
166
+ }
167
+ if (isExploratory) {
168
+ // Exploratory failures are minor and don't trigger streak multipliers
169
+ this.scorecard.exploratory_failure_streak++;
170
+ this.updateScore(-1, `Exploratory Failure: ${toolName}`, 'failure', context);
171
+ return;
172
+ }
173
+ // 3. Constructive Failure (Risky or failed writes)
174
+ let delta = 0;
175
+ switch (type) {
176
+ case 'tool':
177
+ delta = penalties.tool_failure_base;
178
+ break;
179
+ case 'risky':
180
+ delta = penalties.risky_failure_base;
181
+ break;
182
+ case 'bypass':
183
+ delta = penalties.gate_bypass_attempt;
184
+ break;
185
+ }
186
+ this.scorecard.failure_streak++;
187
+ this.scorecard.success_streak = 0;
188
+ // Safety cap: streak multiplier only applies up to 5 consecutive failures
189
+ // to prevent "death spiral" from cascading errors
190
+ const effectiveStreak = Math.min(this.scorecard.failure_streak, 5);
191
+ if (effectiveStreak > 1) {
192
+ delta += (effectiveStreak - 1) * penalties.failure_streak_multiplier;
193
+ }
194
+ if (delta < penalties.max_penalty)
195
+ delta = penalties.max_penalty;
196
+ this.updateScore(delta, `Failure: ${toolName || type}`, 'failure', context);
197
+ }
198
+ updateScore(delta, reason, type, context) {
199
+ const oldScore = this.scorecard.trust_score;
200
+ this.scorecard.trust_score += delta;
201
+ if (this.scorecard.trust_score < 0)
202
+ this.scorecard.trust_score = 0;
203
+ if (this.scorecard.trust_score > 100)
204
+ this.scorecard.trust_score = 100;
205
+ this.scorecard.last_updated = new Date().toISOString();
206
+ if (!this.scorecard.history)
207
+ this.scorecard.history = [];
208
+ this.scorecard.history.push({ type, delta, reason, timestamp: new Date().toISOString() });
209
+ if (context?.sessionId) {
210
+ const eventLog = EventLogService.get(this.stateDir);
211
+ eventLog.recordTrustChange(context.sessionId, { previousScore: oldScore, newScore: this.scorecard.trust_score, delta, reason });
212
+ }
213
+ const limit = this.trustSettings.history_limit || 50;
214
+ if (this.scorecard.history.length > limit) {
215
+ this.scorecard.history.shift();
216
+ }
217
+ this.saveScorecard();
218
+ }
219
+ resetTrust(newScore) {
220
+ const settings = this.trustSettings;
221
+ const now = new Date();
222
+ const coldStartEnd = new Date(now.getTime() + settings.cold_start.cold_start_period_ms);
223
+ this.scorecard.trust_score = newScore ?? settings.cold_start.initial_trust;
224
+ this.scorecard.success_streak = 0;
225
+ this.scorecard.failure_streak = 0;
226
+ this.scorecard.exploratory_failure_streak = 0;
227
+ this.scorecard.grace_failures_remaining = settings.cold_start.grace_failures;
228
+ this.scorecard.last_updated = now.toISOString();
229
+ this.scorecard.first_activity_at = now.toISOString();
230
+ this.scorecard.cold_start_end = coldStartEnd.toISOString();
231
+ this.scorecard.history.push({ type: 'success', delta: 0, reason: 'Manual trust reset (Spiritual Cleanse)', timestamp: now.toISOString() });
232
+ this.saveScorecard();
233
+ }
234
+ getStatusSummary() {
235
+ const scorecard = this.scorecard;
236
+ const successRate = this.calculateSuccessRate(scorecard);
237
+ return {
238
+ stage: this.getStage(),
239
+ successRate,
240
+ isInColdStart: this.isColdStart(),
241
+ graceRemaining: scorecard.grace_failures_remaining ?? 0,
242
+ currentStreak: {
243
+ type: scorecard.success_streak > scorecard.failure_streak ? 'success' : 'failure',
244
+ count: Math.max(scorecard.success_streak, scorecard.failure_streak)
245
+ }
246
+ };
247
+ }
248
+ calculateSuccessRate(scorecard) {
249
+ if (!scorecard.history || scorecard.history.length === 0)
250
+ return 100;
251
+ const recent = scorecard.history.slice(-20);
252
+ const successes = recent.filter(h => h.type === 'success').length;
253
+ return Math.round((successes / recent.length) * 100);
254
+ }
255
+ }
256
+ export function recordSuccess(workspaceDir, reason, context, isSubagent = false) {
257
+ new TrustEngine(workspaceDir).recordSuccess(reason, context, isSubagent);
258
+ }
259
+ export function recordFailure(type, workspaceDir, ctx) {
260
+ new TrustEngine(workspaceDir).recordFailure(type, ctx);
261
+ }
262
+ export function getAgentScorecard(workspaceDir) {
263
+ return new TrustEngine(workspaceDir).getScorecard();
264
+ }
265
+ export function getTrustStats(scorecard) {
266
+ const dummy = new TrustEngine(process.cwd());
267
+ const successRate = dummy.calculateSuccessRate(scorecard);
268
+ const stages = { stage_1_observer: 30, stage_2_editor: 60, stage_3_developer: 80 };
269
+ let stage = scorecard.trust_score < stages.stage_1_observer ? 1 :
270
+ scorecard.trust_score < stages.stage_2_editor ? 2 :
271
+ scorecard.trust_score < stages.stage_3_developer ? 3 : 4;
272
+ return {
273
+ stage, successRate,
274
+ isInColdStart: scorecard.cold_start_end ? new Date() < new Date(scorecard.cold_start_end) : false,
275
+ graceRemaining: scorecard.grace_failures_remaining ?? 0,
276
+ currentStreak: {
277
+ type: (scorecard.success_streak || 0) > (scorecard.failure_streak || 0) ? 'success' : 'failure',
278
+ count: Math.max(scorecard.success_streak || 0, scorecard.failure_streak || 0)
279
+ }
280
+ };
281
+ }
282
+ export function getTrustStatus(workspaceDir) {
283
+ return new TrustEngine(workspaceDir).getStatusSummary();
284
+ }
@@ -0,0 +1,64 @@
1
+ import { PD_FILES } from './paths.js';
2
+ import { PainConfig } from './config.js';
3
+ import { EventLog } from './event-log.js';
4
+ import { PainDictionary } from './dictionary.js';
5
+ import { TrustEngine } from './trust-engine.js';
6
+ import { HygieneTracker } from './hygiene/tracker.js';
7
+ /**
8
+ * WorkspaceContext - Centralized management of workspace-specific paths and services.
9
+ * Implements a cached singleton pattern per workspace directory.
10
+ */
11
+ export declare class WorkspaceContext {
12
+ private static instances;
13
+ private static pathResolver;
14
+ readonly workspaceDir: string;
15
+ readonly stateDir: string;
16
+ private _config?;
17
+ private _eventLog?;
18
+ private _dictionary?;
19
+ private _trust?;
20
+ private _hygiene?;
21
+ private constructor();
22
+ /**
23
+ * Governance configuration for this workspace.
24
+ */
25
+ get config(): PainConfig;
26
+ /**
27
+ * Event logging service for this workspace.
28
+ */
29
+ get eventLog(): EventLog;
30
+ /**
31
+ * Pain dictionary service for this workspace.
32
+ */
33
+ get dictionary(): PainDictionary;
34
+ /**
35
+ * Trust engine service bound to this workspace.
36
+ */
37
+ get trust(): TrustEngine;
38
+ /**
39
+ * Hygiene tracking service for this workspace.
40
+ */
41
+ get hygiene(): HygieneTracker;
42
+ /**
43
+ * Creates or retrieves a WorkspaceContext instance from an OpenClaw hook context.
44
+ * Uses PathResolver to handle path normalization and fallback logic.
45
+ * @throws Error if workspaceDir is missing and no fallback available.
46
+ */
47
+ static fromHookContext(ctx: any): WorkspaceContext;
48
+ /**
49
+ * Resolves a PD file path within the workspace.
50
+ */
51
+ resolve(fileKey: keyof typeof PD_FILES): string;
52
+ /**
53
+ * Resets internal caches for services and paths.
54
+ */
55
+ invalidate(): void;
56
+ /**
57
+ * Removes a workspace from the cache.
58
+ */
59
+ static dispose(workspaceDir: string): void;
60
+ /**
61
+ * Clears the instance cache (primarily for testing).
62
+ */
63
+ static clearCache(): void;
64
+ }
@@ -0,0 +1,134 @@
1
+ import { resolvePdPath } from './paths.js';
2
+ import { PathResolver } from './path-resolver.js';
3
+ import { ConfigService } from './config-service.js';
4
+ import { EventLogService } from './event-log.js';
5
+ import { DictionaryService } from './dictionary-service.js';
6
+ import { TrustEngine } from './trust-engine.js';
7
+ import { HygieneTracker } from './hygiene/tracker.js';
8
+ /**
9
+ * WorkspaceContext - Centralized management of workspace-specific paths and services.
10
+ * Implements a cached singleton pattern per workspace directory.
11
+ */
12
+ export class WorkspaceContext {
13
+ static instances = new Map();
14
+ static pathResolver = new PathResolver();
15
+ workspaceDir;
16
+ stateDir;
17
+ _config;
18
+ _eventLog;
19
+ _dictionary;
20
+ _trust;
21
+ _hygiene;
22
+ constructor(workspaceDir, stateDir) {
23
+ this.workspaceDir = workspaceDir;
24
+ this.stateDir = stateDir;
25
+ }
26
+ /**
27
+ * Governance configuration for this workspace.
28
+ */
29
+ get config() {
30
+ if (!this._config) {
31
+ this._config = ConfigService.get(this.stateDir);
32
+ }
33
+ return this._config;
34
+ }
35
+ /**
36
+ * Event logging service for this workspace.
37
+ */
38
+ get eventLog() {
39
+ if (!this._eventLog) {
40
+ this._eventLog = EventLogService.get(this.stateDir);
41
+ }
42
+ return this._eventLog;
43
+ }
44
+ /**
45
+ * Pain dictionary service for this workspace.
46
+ */
47
+ get dictionary() {
48
+ if (!this._dictionary) {
49
+ this._dictionary = DictionaryService.get(this.stateDir);
50
+ }
51
+ return this._dictionary;
52
+ }
53
+ /**
54
+ * Trust engine service bound to this workspace.
55
+ */
56
+ get trust() {
57
+ if (!this._trust) {
58
+ this._trust = new TrustEngine(this.workspaceDir);
59
+ }
60
+ return this._trust;
61
+ }
62
+ /**
63
+ * Hygiene tracking service for this workspace.
64
+ */
65
+ get hygiene() {
66
+ if (!this._hygiene) {
67
+ this._hygiene = new HygieneTracker(this.stateDir);
68
+ }
69
+ return this._hygiene;
70
+ }
71
+ /**
72
+ * Creates or retrieves a WorkspaceContext instance from an OpenClaw hook context.
73
+ * Uses PathResolver to handle path normalization and fallback logic.
74
+ * @throws Error if workspaceDir is missing and no fallback available.
75
+ */
76
+ static fromHookContext(ctx) {
77
+ let workspaceDir = ctx.workspaceDir;
78
+ if (!workspaceDir) {
79
+ console.warn('[PD:WorkspaceContext] workspaceDir not provided in context, using PathResolver fallback');
80
+ workspaceDir = this.pathResolver.getWorkspaceDir();
81
+ console.log(`[PD:WorkspaceContext] Resolved workspaceDir to: ${workspaceDir}`);
82
+ }
83
+ else {
84
+ const normalized = this.pathResolver.normalizeWorkspacePath(workspaceDir);
85
+ if (normalized !== workspaceDir) {
86
+ console.log(`[PD:WorkspaceContext] Normalized workspaceDir: ${workspaceDir} -> ${normalized}`);
87
+ workspaceDir = normalized;
88
+ }
89
+ }
90
+ const existing = this.instances.get(workspaceDir);
91
+ if (existing)
92
+ return existing;
93
+ let stateDir = ctx.stateDir;
94
+ if (!stateDir) {
95
+ stateDir = resolvePdPath(workspaceDir, 'STATE_DIR');
96
+ console.log(`[PD:WorkspaceContext] Computed stateDir: ${stateDir}`);
97
+ }
98
+ const instance = new WorkspaceContext(workspaceDir, stateDir);
99
+ this.instances.set(workspaceDir, instance);
100
+ console.log(`[PD:WorkspaceContext] Created new context for workspace: ${workspaceDir}`);
101
+ return instance;
102
+ }
103
+ /**
104
+ * Resolves a PD file path within the workspace.
105
+ */
106
+ resolve(fileKey) {
107
+ return resolvePdPath(this.workspaceDir, fileKey);
108
+ }
109
+ /**
110
+ * Resets internal caches for services and paths.
111
+ */
112
+ invalidate() {
113
+ this._config = undefined;
114
+ this._eventLog = undefined;
115
+ this._dictionary = undefined;
116
+ this._trust = undefined;
117
+ }
118
+ /**
119
+ * Removes a workspace from the cache.
120
+ */
121
+ static dispose(workspaceDir) {
122
+ const instance = this.instances.get(workspaceDir);
123
+ if (instance) {
124
+ instance.invalidate();
125
+ this.instances.delete(workspaceDir);
126
+ }
127
+ }
128
+ /**
129
+ * Clears the instance cache (primarily for testing).
130
+ */
131
+ static clearCache() {
132
+ this.instances.clear();
133
+ }
134
+ }
@@ -0,0 +1,6 @@
1
+ import type { PluginHookBeforeToolCallEvent, PluginHookToolContext, PluginHookBeforeToolCallResult } from '../openclaw-sdk.js';
2
+ export declare function handleBeforeToolCall(event: PluginHookBeforeToolCallEvent, ctx: PluginHookToolContext & {
3
+ workspaceDir?: string;
4
+ pluginConfig?: Record<string, unknown>;
5
+ logger?: any;
6
+ }): PluginHookBeforeToolCallResult | void;