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,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;
|