principles-disciple 1.7.5 → 1.7.8

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 (129) hide show
  1. package/dist/commands/context.js +5 -15
  2. package/dist/commands/evolution-status.js +29 -48
  3. package/dist/commands/export.js +61 -8
  4. package/dist/commands/nocturnal-review.d.ts +24 -0
  5. package/dist/commands/nocturnal-review.js +265 -0
  6. package/dist/commands/nocturnal-rollout.d.ts +27 -0
  7. package/dist/commands/nocturnal-rollout.js +671 -0
  8. package/dist/commands/nocturnal-train.d.ts +25 -0
  9. package/dist/commands/nocturnal-train.js +919 -0
  10. package/dist/commands/pain.js +8 -21
  11. package/dist/config/defaults/runtime.d.ts +40 -0
  12. package/dist/config/defaults/runtime.js +44 -0
  13. package/dist/config/errors.d.ts +84 -0
  14. package/dist/config/errors.js +94 -0
  15. package/dist/config/index.d.ts +7 -0
  16. package/dist/config/index.js +7 -0
  17. package/dist/constants/diagnostician.d.ts +0 -4
  18. package/dist/constants/diagnostician.js +0 -4
  19. package/dist/constants/tools.d.ts +2 -2
  20. package/dist/constants/tools.js +1 -1
  21. package/dist/core/adaptive-thresholds.d.ts +186 -0
  22. package/dist/core/adaptive-thresholds.js +300 -0
  23. package/dist/core/config.d.ts +2 -38
  24. package/dist/core/config.js +6 -61
  25. package/dist/core/control-ui-db.d.ts +27 -0
  26. package/dist/core/control-ui-db.js +18 -0
  27. package/dist/core/event-log.d.ts +1 -2
  28. package/dist/core/event-log.js +0 -3
  29. package/dist/core/evolution-engine.js +1 -21
  30. package/dist/core/evolution-reducer.d.ts +7 -1
  31. package/dist/core/evolution-reducer.js +56 -4
  32. package/dist/core/evolution-types.d.ts +61 -9
  33. package/dist/core/evolution-types.js +31 -9
  34. package/dist/core/external-training-contract.d.ts +276 -0
  35. package/dist/core/external-training-contract.js +269 -0
  36. package/dist/core/local-worker-routing.d.ts +175 -0
  37. package/dist/core/local-worker-routing.js +525 -0
  38. package/dist/core/model-deployment-registry.d.ts +218 -0
  39. package/dist/core/model-deployment-registry.js +503 -0
  40. package/dist/core/model-training-registry.d.ts +295 -0
  41. package/dist/core/model-training-registry.js +475 -0
  42. package/dist/core/nocturnal-arbiter.d.ts +159 -0
  43. package/dist/core/nocturnal-arbiter.js +534 -0
  44. package/dist/core/nocturnal-candidate-scoring.d.ts +137 -0
  45. package/dist/core/nocturnal-candidate-scoring.js +266 -0
  46. package/dist/core/nocturnal-compliance.d.ts +175 -0
  47. package/dist/core/nocturnal-compliance.js +824 -0
  48. package/dist/core/nocturnal-dataset.d.ts +224 -0
  49. package/dist/core/nocturnal-dataset.js +443 -0
  50. package/dist/core/nocturnal-executability.d.ts +85 -0
  51. package/dist/core/nocturnal-executability.js +331 -0
  52. package/dist/core/nocturnal-export.d.ts +124 -0
  53. package/dist/core/nocturnal-export.js +275 -0
  54. package/dist/core/nocturnal-paths.d.ts +124 -0
  55. package/dist/core/nocturnal-paths.js +214 -0
  56. package/dist/core/nocturnal-trajectory-extractor.d.ts +242 -0
  57. package/dist/core/nocturnal-trajectory-extractor.js +307 -0
  58. package/dist/core/nocturnal-trinity.d.ts +311 -0
  59. package/dist/core/nocturnal-trinity.js +880 -0
  60. package/dist/core/path-resolver.js +2 -1
  61. package/dist/core/paths.d.ts +6 -0
  62. package/dist/core/paths.js +6 -0
  63. package/dist/core/principle-training-state.d.ts +121 -0
  64. package/dist/core/principle-training-state.js +321 -0
  65. package/dist/core/promotion-gate.d.ts +238 -0
  66. package/dist/core/promotion-gate.js +529 -0
  67. package/dist/core/session-tracker.d.ts +10 -0
  68. package/dist/core/session-tracker.js +14 -0
  69. package/dist/core/shadow-observation-registry.d.ts +217 -0
  70. package/dist/core/shadow-observation-registry.js +308 -0
  71. package/dist/core/training-program.d.ts +233 -0
  72. package/dist/core/training-program.js +433 -0
  73. package/dist/core/trajectory.d.ts +155 -1
  74. package/dist/core/trajectory.js +292 -8
  75. package/dist/core/workspace-context.d.ts +0 -6
  76. package/dist/core/workspace-context.js +0 -12
  77. package/dist/hooks/bash-risk.d.ts +57 -0
  78. package/dist/hooks/bash-risk.js +137 -0
  79. package/dist/hooks/edit-verification.d.ts +62 -0
  80. package/dist/hooks/edit-verification.js +256 -0
  81. package/dist/hooks/gate-block-helper.d.ts +44 -0
  82. package/dist/hooks/gate-block-helper.js +119 -0
  83. package/dist/hooks/gate.d.ts +18 -0
  84. package/dist/hooks/gate.js +62 -751
  85. package/dist/hooks/gfi-gate.d.ts +40 -0
  86. package/dist/hooks/gfi-gate.js +113 -0
  87. package/dist/hooks/pain.js +6 -9
  88. package/dist/hooks/progressive-trust-gate.d.ts +51 -0
  89. package/dist/hooks/progressive-trust-gate.js +89 -0
  90. package/dist/hooks/prompt.d.ts +11 -11
  91. package/dist/hooks/prompt.js +167 -77
  92. package/dist/hooks/subagent.js +43 -6
  93. package/dist/hooks/thinking-checkpoint.d.ts +37 -0
  94. package/dist/hooks/thinking-checkpoint.js +51 -0
  95. package/dist/http/principles-console-route.js +13 -3
  96. package/dist/i18n/commands.js +8 -8
  97. package/dist/index.js +129 -28
  98. package/dist/service/central-database.js +2 -1
  99. package/dist/service/control-ui-query-service.d.ts +1 -1
  100. package/dist/service/control-ui-query-service.js +3 -3
  101. package/dist/service/evolution-query-service.d.ts +1 -1
  102. package/dist/service/evolution-query-service.js +5 -5
  103. package/dist/service/evolution-worker.d.ts +52 -4
  104. package/dist/service/evolution-worker.js +328 -16
  105. package/dist/service/nocturnal-runtime.d.ts +183 -0
  106. package/dist/service/nocturnal-runtime.js +352 -0
  107. package/dist/service/nocturnal-service.d.ts +163 -0
  108. package/dist/service/nocturnal-service.js +787 -0
  109. package/dist/service/nocturnal-target-selector.d.ts +145 -0
  110. package/dist/service/nocturnal-target-selector.js +315 -0
  111. package/dist/service/phase3-input-filter.d.ts +48 -12
  112. package/dist/service/phase3-input-filter.js +84 -18
  113. package/dist/service/runtime-summary-service.d.ts +34 -10
  114. package/dist/service/runtime-summary-service.js +87 -48
  115. package/dist/tools/deep-reflect.js +2 -1
  116. package/dist/types/event-types.d.ts +4 -10
  117. package/dist/types/runtime-summary.d.ts +47 -0
  118. package/dist/types/runtime-summary.js +1 -0
  119. package/dist/types.d.ts +0 -3
  120. package/dist/types.js +0 -2
  121. package/openclaw.plugin.json +1 -1
  122. package/package.json +1 -1
  123. package/templates/langs/en/skills/pd-mentor/SKILL.md +5 -5
  124. package/templates/langs/zh/skills/pd-mentor/SKILL.md +5 -5
  125. package/templates/pain_settings.json +0 -6
  126. package/dist/commands/trust.d.ts +0 -4
  127. package/dist/commands/trust.js +0 -78
  128. package/dist/core/trust-engine.d.ts +0 -96
  129. package/dist/core/trust-engine.js +0 -286
@@ -32,7 +32,7 @@ disable-model-invocation: true
32
32
  | `/pd-thinking` | 管理思维模型与候选方案 | 元认知管理 |
33
33
  | `/pd-evolve` | 执行完整进化循环 | 问题修复 |
34
34
  | `/pd-daily` | 配置并发送进化日报 | 日常查看 |
35
- | `/pd-trust` | 查看信任积分与安全等级 | 权限查询 |
35
+ | `/pd-evolution-status` | 查看EP等级与安全状态 | 状态查询 |
36
36
  | `/pd-status` | 查看系统状态(GFI和痛苦词典) | 健康检查 |
37
37
  | `/pd-grooming` | 工作区数字大扫除 | 熵减维护 |
38
38
  | `/pd-help` | 获取交互式命令引导 | 本技能 |
@@ -72,7 +72,7 @@ disable-model-invocation: true
72
72
 
73
73
  **推荐流程**:
74
74
  1. `/pd-daily` - 发送今日进化日报
75
- 2. `/pd-trust` - 查看当前信任积分
75
+ 2. `/pd-evolution-status` - 查看当前信任积分
76
76
  3. `/pd-okr` - 检查 OKR 对齐情况
77
77
 
78
78
  **话术**: "日报在手,进化我有。让我帮你回顾今天的成果。"
@@ -95,7 +95,7 @@ disable-model-invocation: true
95
95
  **触发条件**: 用户说"权限不够"、"被拦截了"、"安全等级"
96
96
 
97
97
  **推荐流程**:
98
- 1. `/pd-trust` - 查看信任积分和安全等级
98
+ 1. `/pd-evolution-status` - 查看信任积分和安全等级
99
99
  2. 解释当前等级的能力边界
100
100
 
101
101
  **话术**: "信任是挣来的,不是给的。让我帮你了解当前的安全等级。"
@@ -207,8 +207,8 @@ disable-model-invocation: true
207
207
  **Q: 什么是 GFI(摩擦指数)?**
208
208
  A: GFI (Global Friction Index) 衡量系统的"痛苦程度",分值范围 0-100。值越高表示摩擦越大,需要关注。
209
209
 
210
- **Q: 信任积分如何计算?**
211
- A: 基于成功操作累积积分,失败操作扣分。达到阈值后自动升级安全等级,解锁更多权限。
210
+ **Q: EP (进化积分) 如何计算?**
211
+ A: EP (Evolution Points) 通过成功完成任务和问题修复累积。失败会扣除EP但有保护机制。达到阈值后自动升级EP等级,解锁更多权限(更大的代码修改限额)。
212
212
 
213
213
  **Q: Pain Signal 是什么?**
214
214
  A: Pain Signal 是系统检测到的问题信号,存储在 `.state/.pain_flag` 中。触发后系统会启动进化循环。
@@ -103,12 +103,6 @@
103
103
  "large_change_block": 50
104
104
  },
105
105
  "large_change_lines": 50,
106
- "trust_stage_multipliers": {
107
- "1": 0.5,
108
- "2": 0.75,
109
- "3": 1.0,
110
- "4": 1.5
111
- },
112
106
  "bash_safe_patterns": [
113
107
  "^(ls|dir|pwd|which|where|echo|env|cat|type|head|tail|less|more)\\b",
114
108
  "^git\\s+(status|log|diff|branch|show|remote)\\b",
@@ -1,4 +0,0 @@
1
- import type { PluginHookAgentContext } from '../openclaw-sdk.js';
2
- export declare function handleTrustCommand(ctx: PluginHookAgentContext & {
3
- workspaceDir?: string;
4
- }): string;
@@ -1,78 +0,0 @@
1
- import { WorkspaceContext } from '../core/workspace-context.js';
2
- function createProgressBar(value, max, length = 12) {
3
- const safeValue = Math.max(0, Math.min(max, value));
4
- const filledLength = Math.round((safeValue / max) * length);
5
- const emptyLength = Math.max(0, length - filledLength);
6
- return `[${'#'.repeat(filledLength)}${'-'.repeat(emptyLength)}]`;
7
- }
8
- function formatStageTitle(stage, isZh) {
9
- const titles = isZh
10
- ? ['Observer', 'Editor', 'Developer', 'Architect']
11
- : ['Observer', 'Editor', 'Developer', 'Architect'];
12
- return titles[Math.max(0, Math.min(3, stage - 1))] ?? 'Unknown';
13
- }
14
- export function handleTrustCommand(ctx) {
15
- const { workspaceDir } = ctx;
16
- if (!workspaceDir)
17
- return 'Error: Workspace directory not found.';
18
- const wctx = WorkspaceContext.fromHookContext(ctx);
19
- const trustEngine = wctx.trust;
20
- const trustScore = trustEngine.getScore();
21
- const stage = trustEngine.getStage();
22
- const isZh = wctx.config.get('language') === 'zh';
23
- const scorecard = trustEngine.getScorecard();
24
- const hygiene = wctx.hygiene.getStats();
25
- const trustBar = createProgressBar(trustScore, 100);
26
- const hygieneScore = Math.min(100, hygiene.persistenceCount * 10);
27
- const hygieneBar = createProgressBar(hygieneScore, 100);
28
- const rewardPolicy = scorecard.reward_policy ?? 'frozen_all_positive';
29
- const lastUpdated = scorecard.last_updated ?? '--';
30
- if (isZh) {
31
- return [
32
- 'Principles Disciple - Legacy Trust',
33
- '=================================',
34
- '',
35
- `- Legacy Trust: ${trustBar} ${trustScore}/100`,
36
- `- Stage: ${stage} (${formatStageTitle(stage, true)})`,
37
- `- Status: legacy/frozen`,
38
- `- Reward Policy: ${rewardPolicy}`,
39
- `- Last Updated: ${lastUpdated}`,
40
- '',
41
- '说明',
42
- '- 这是兼容旧控制面的 legacy trust 视图,不再代表新的长期能力模型。',
43
- '- `tool_success` 与 `subagent_success` 不再自动提升 trust。',
44
- '- 当前观察窗口内,trust 主要保留为兼容读数与 gate 兼容输入。',
45
- '',
46
- '认知卫生',
47
- `- Persistence: ${hygieneBar} ${hygiene.persistenceCount} actions today`,
48
- `- Total Chars Persisted: ${hygiene.totalCharsPersisted}`,
49
- `- Workspace Grooming: ${hygiene.groomingExecutedCount > 0 ? 'done' : 'pending'}`,
50
- '',
51
- '下一步',
52
- '- 在进入 Phase 3 capability shadow 之前,先看 production observation 数据是否稳定。',
53
- ].join('\n');
54
- }
55
- return [
56
- 'Principles Disciple - Legacy Trust',
57
- '=================================',
58
- '',
59
- `- Legacy Trust: ${trustBar} ${trustScore}/100`,
60
- `- Stage: ${stage} (${formatStageTitle(stage, false)})`,
61
- '- Status: legacy/frozen',
62
- `- Reward Policy: ${rewardPolicy}`,
63
- `- Last Updated: ${lastUpdated}`,
64
- '',
65
- 'Notes',
66
- '- This is a compatibility view of legacy trust, not the future long-term capability model.',
67
- '- `tool_success` and `subagent_success` no longer raise trust automatically.',
68
- '- During the current observation window, trust remains mainly for compatibility reads and gate compatibility.',
69
- '',
70
- 'Cognitive Hygiene',
71
- `- Persistence: ${hygieneBar} ${hygiene.persistenceCount} actions today`,
72
- `- Total Chars Persisted: ${hygiene.totalCharsPersisted}`,
73
- `- Workspace Grooming: ${hygiene.groomingExecutedCount > 0 ? 'done' : 'pending'}`,
74
- '',
75
- 'Next',
76
- '- Do not move into Phase 3 capability shadow until production observation data is stable.',
77
- ].join('\n');
78
- }
@@ -1,96 +0,0 @@
1
- /**
2
- * Trust Engine V2.1 - Recalibrated for Action Classification
3
- * Differentiates between Exploratory (safe) and Constructive (risky) actions.
4
- */
5
- export interface TrustScorecard {
6
- trust_score: number;
7
- success_streak: number;
8
- failure_streak: number;
9
- exploratory_failure_streak: number;
10
- grace_failures_remaining?: number;
11
- last_updated: string;
12
- cold_start_end?: string;
13
- first_activity_at?: string;
14
- history: Array<{
15
- type: 'success' | 'failure' | 'penalty' | 'info';
16
- delta: number;
17
- reason: string;
18
- timestamp: string;
19
- }>;
20
- frozen?: boolean;
21
- reward_policy?: 'frozen_all_positive' | 'frozen_atomic_positive_keep_plan_ready';
22
- }
23
- export type TrustStage = 1 | 2 | 3 | 4;
24
- export declare const EXPLORATORY_TOOLS: Set<string>;
25
- export declare class TrustEngine {
26
- private scorecard;
27
- private workspaceDir;
28
- private stateDir;
29
- constructor(workspaceDir: string);
30
- private get config();
31
- private get trustSettings();
32
- private loadScorecard;
33
- private applyLegacyFreezeMetadata;
34
- private saveScorecard;
35
- getScore(): number;
36
- getScorecard(): TrustScorecard;
37
- getStage(): TrustStage;
38
- isColdStart(): boolean;
39
- recordSuccess(reason: string, context?: {
40
- sessionId?: string;
41
- api?: any;
42
- toolName?: string;
43
- }, isSubagent?: boolean): void;
44
- recordFailure(type: 'tool' | 'risky' | 'bypass', context: {
45
- sessionId?: string;
46
- api?: any;
47
- toolName?: string;
48
- error?: string;
49
- }): void;
50
- private touchScorecard;
51
- private updateScore;
52
- resetTrust(newScore?: number): void;
53
- getStatusSummary(): {
54
- stage: TrustStage;
55
- successRate: number;
56
- isInColdStart: boolean;
57
- graceRemaining: number;
58
- currentStreak: {
59
- type: string;
60
- count: number;
61
- };
62
- };
63
- private calculateSuccessRate;
64
- }
65
- export declare function recordSuccess(workspaceDir: string, reason: string, context?: {
66
- sessionId?: string;
67
- api?: any;
68
- toolName?: string;
69
- }, isSubagent?: boolean): void;
70
- export declare function recordFailure(type: 'tool' | 'risky' | 'bypass', workspaceDir: string, ctx: {
71
- sessionId?: string;
72
- api?: any;
73
- toolName?: string;
74
- error?: string;
75
- }): void;
76
- export declare function getAgentScorecard(workspaceDir: string): TrustScorecard;
77
- export declare function getTrustStats(scorecard: TrustScorecard): {
78
- stage: TrustStage;
79
- successRate: any;
80
- isInColdStart: boolean;
81
- graceRemaining: number;
82
- currentStreak: {
83
- type: string;
84
- count: number;
85
- };
86
- };
87
- export declare function getTrustStatus(workspaceDir: string): {
88
- stage: TrustStage;
89
- successRate: number;
90
- isInColdStart: boolean;
91
- graceRemaining: number;
92
- currentStreak: {
93
- type: string;
94
- count: number;
95
- };
96
- };
@@ -1,286 +0,0 @@
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
- import { TrajectoryRegistry } from './trajectory.js';
11
- import { EXPLORATORY_TOOLS as SHARED_EXPLORATORY_TOOLS } from '../constants/tools.js';
12
- export const EXPLORATORY_TOOLS = new Set(SHARED_EXPLORATORY_TOOLS);
13
- const LEGACY_TRUST_REWARD_POLICY = 'frozen_all_positive';
14
- export class TrustEngine {
15
- scorecard;
16
- workspaceDir;
17
- stateDir;
18
- constructor(workspaceDir) {
19
- this.workspaceDir = workspaceDir;
20
- this.stateDir = resolvePdPath(workspaceDir, 'STATE_DIR');
21
- this.scorecard = this.loadScorecard();
22
- const scorecardPath = resolvePdPath(this.workspaceDir, 'AGENT_SCORECARD');
23
- if (!fs.existsSync(scorecardPath)) {
24
- this.saveScorecard();
25
- }
26
- }
27
- get config() {
28
- return ConfigService.get(this.stateDir);
29
- }
30
- get trustSettings() {
31
- const settings = this.config.get('trust');
32
- return settings || {
33
- stages: { stage_1_observer: 30, stage_2_editor: 60, stage_3_developer: 80 },
34
- cold_start: { initial_trust: 85, grace_failures: 5, cold_start_period_ms: 86400000 },
35
- // BUGFIX #84: Reduced penalties to prevent Trust collapse
36
- penalties: { tool_failure_base: -1, risky_failure_base: -5, gate_bypass_attempt: -3, failure_streak_multiplier: -1, max_penalty: -10 },
37
- 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 },
38
- limits: { stage_2_max_lines: 50, stage_3_max_lines: 300 }
39
- };
40
- }
41
- loadScorecard() {
42
- const scorecardPath = resolvePdPath(this.workspaceDir, 'AGENT_SCORECARD');
43
- const settings = this.trustSettings;
44
- if (fs.existsSync(scorecardPath)) {
45
- try {
46
- const raw = fs.readFileSync(scorecardPath, 'utf8');
47
- const data = JSON.parse(raw);
48
- if (data.score !== undefined && data.trust_score === undefined)
49
- data.trust_score = data.score;
50
- if (!data.history)
51
- data.history = [];
52
- if (data.exploratory_failure_streak === undefined)
53
- data.exploratory_failure_streak = 0;
54
- this.applyLegacyFreezeMetadata(data);
55
- return data;
56
- }
57
- catch (e) {
58
- console.error(`[PD:TrustEngine] FATAL: Failed to parse scorecard at ${scorecardPath}. Resetting.`);
59
- }
60
- }
61
- const now = new Date();
62
- const coldStartEnd = new Date(now.getTime() + settings.cold_start.cold_start_period_ms);
63
- const scorecard = {
64
- trust_score: settings.cold_start.initial_trust,
65
- success_streak: 0,
66
- failure_streak: 0,
67
- exploratory_failure_streak: 0,
68
- grace_failures_remaining: settings.cold_start.grace_failures,
69
- last_updated: now.toISOString(),
70
- cold_start_end: coldStartEnd.toISOString(),
71
- first_activity_at: now.toISOString(),
72
- history: []
73
- };
74
- this.applyLegacyFreezeMetadata(scorecard);
75
- return scorecard;
76
- }
77
- applyLegacyFreezeMetadata(scorecard) {
78
- scorecard.frozen = true;
79
- scorecard.reward_policy = LEGACY_TRUST_REWARD_POLICY;
80
- }
81
- saveScorecard() {
82
- const scorecardPath = resolvePdPath(this.workspaceDir, 'AGENT_SCORECARD');
83
- try {
84
- const dir = path.dirname(scorecardPath);
85
- if (!fs.existsSync(dir))
86
- fs.mkdirSync(dir, { recursive: true });
87
- fs.writeFileSync(scorecardPath, JSON.stringify(this.scorecard, null, 2), 'utf8');
88
- }
89
- catch (e) {
90
- console.error(`[PD:TrustEngine] Failed to save scorecard: ${String(e)}`);
91
- }
92
- }
93
- getScore() { return this.scorecard.trust_score; }
94
- getScorecard() { return this.scorecard; }
95
- getStage() {
96
- const score = this.scorecard.trust_score;
97
- const stages = this.trustSettings.stages;
98
- if (score < stages.stage_1_observer)
99
- return 1;
100
- if (score < stages.stage_2_editor)
101
- return 2;
102
- if (score < stages.stage_3_developer)
103
- return 3;
104
- return 4;
105
- }
106
- isColdStart() {
107
- if (!this.scorecard.cold_start_end)
108
- return false;
109
- return new Date() < new Date(this.scorecard.cold_start_end);
110
- }
111
- recordSuccess(reason, context, isSubagent = false) {
112
- const toolName = context?.toolName;
113
- // 1. Check if this is an exploratory tool success
114
- const isExploratory = toolName ? EXPLORATORY_TOOLS.has(toolName) : false;
115
- if (reason === 'tool_success' && isExploratory) {
116
- this.scorecard.exploratory_failure_streak = 0;
117
- this.touchScorecard();
118
- return;
119
- }
120
- // Phase 1 freeze: do not let atomic successes inflate legacy trust.
121
- this.scorecard.success_streak = 0;
122
- this.scorecard.failure_streak = 0; // Reset failure streak on constructive success
123
- this.scorecard.exploratory_failure_streak = 0;
124
- this.touchScorecard();
125
- }
126
- recordFailure(type, context) {
127
- const settings = this.trustSettings;
128
- const penalties = settings.penalties;
129
- const toolName = context?.toolName;
130
- // 1. Classification: Is this an exploratory failure?
131
- const isExploratory = toolName ? EXPLORATORY_TOOLS.has(toolName) : false;
132
- // 2. Cold start grace (only for non-risky actions)
133
- if (type !== 'risky' && this.isColdStart() && (this.scorecard.grace_failures_remaining || 0) > 0) {
134
- this.scorecard.grace_failures_remaining = (this.scorecard.grace_failures_remaining || 0) - 1;
135
- this.updateScore(0, `Grace Failure consumed (${toolName || type})`, 'failure', context);
136
- return;
137
- }
138
- if (isExploratory) {
139
- // Exploratory failures are minor and don't trigger streak multipliers
140
- this.scorecard.exploratory_failure_streak++;
141
- this.updateScore(-1, `Exploratory Failure: ${toolName}`, 'failure', context);
142
- return;
143
- }
144
- // BUGFIX #84: sessions_send timeout should not be penalized
145
- // Communication timeouts are not agent failures - the message may have been delivered
146
- const errorStr = String(context?.error || '');
147
- if (toolName === 'sessions_send' && (errorStr.includes('timeout') || errorStr === 'timeout')) {
148
- this.updateScore(0, `Communication timeout (sessions_send): ignored`, 'info', context);
149
- return;
150
- }
151
- // 3. Constructive Failure (Risky or failed writes)
152
- let delta = 0;
153
- switch (type) {
154
- case 'tool':
155
- delta = penalties.tool_failure_base;
156
- break;
157
- case 'risky':
158
- delta = penalties.risky_failure_base;
159
- break;
160
- case 'bypass':
161
- delta = penalties.gate_bypass_attempt;
162
- break;
163
- }
164
- this.scorecard.failure_streak++;
165
- this.scorecard.success_streak = 0;
166
- // Safety cap: streak multiplier only applies up to 5 consecutive failures
167
- // to prevent "death spiral" from cascading errors
168
- const effectiveStreak = Math.min(this.scorecard.failure_streak, 5);
169
- if (effectiveStreak > 1) {
170
- delta += (effectiveStreak - 1) * penalties.failure_streak_multiplier;
171
- }
172
- if (delta < penalties.max_penalty)
173
- delta = penalties.max_penalty;
174
- this.updateScore(delta, `Failure: ${toolName || type}`, 'failure', context);
175
- }
176
- touchScorecard() {
177
- this.applyLegacyFreezeMetadata(this.scorecard);
178
- this.scorecard.last_updated = new Date().toISOString();
179
- this.saveScorecard();
180
- }
181
- updateScore(delta, reason, type, context) {
182
- const oldScore = this.scorecard.trust_score;
183
- this.applyLegacyFreezeMetadata(this.scorecard);
184
- this.scorecard.trust_score += delta;
185
- // Floor score: never drop below 30 (prevents Trust collapse from cascades)
186
- if (this.scorecard.trust_score < 30)
187
- this.scorecard.trust_score = 30;
188
- if (this.scorecard.trust_score > 100)
189
- this.scorecard.trust_score = 100;
190
- this.scorecard.last_updated = new Date().toISOString();
191
- if (!this.scorecard.history)
192
- this.scorecard.history = [];
193
- this.scorecard.history.push({ type, delta, reason, timestamp: new Date().toISOString() });
194
- if (context?.sessionId) {
195
- const eventLog = EventLogService.get(this.stateDir);
196
- eventLog.recordTrustChange(context.sessionId, { previousScore: oldScore, newScore: this.scorecard.trust_score, delta, reason });
197
- }
198
- if (context?.sessionId) {
199
- try {
200
- TrajectoryRegistry.use(this.workspaceDir, (trajectory) => {
201
- trajectory.recordTrustChange({
202
- sessionId: context.sessionId,
203
- previousScore: oldScore,
204
- newScore: this.scorecard.trust_score,
205
- delta,
206
- reason,
207
- });
208
- });
209
- }
210
- catch {
211
- // Do not block trust updates if trajectory storage is unavailable.
212
- }
213
- }
214
- const limit = this.trustSettings.history_limit || 50;
215
- if (this.scorecard.history.length > limit) {
216
- this.scorecard.history.shift();
217
- }
218
- this.saveScorecard();
219
- }
220
- resetTrust(newScore) {
221
- const settings = this.trustSettings;
222
- const now = new Date();
223
- const coldStartEnd = new Date(now.getTime() + settings.cold_start.cold_start_period_ms);
224
- this.scorecard.trust_score = newScore ?? settings.cold_start.initial_trust;
225
- this.scorecard.success_streak = 0;
226
- this.scorecard.failure_streak = 0;
227
- this.scorecard.exploratory_failure_streak = 0;
228
- this.scorecard.grace_failures_remaining = settings.cold_start.grace_failures;
229
- this.scorecard.last_updated = now.toISOString();
230
- this.scorecard.first_activity_at = now.toISOString();
231
- this.scorecard.cold_start_end = coldStartEnd.toISOString();
232
- this.scorecard.history.push({ type: 'success', delta: 0, reason: 'Manual trust reset (Spiritual Cleanse)', timestamp: now.toISOString() });
233
- this.applyLegacyFreezeMetadata(this.scorecard);
234
- this.saveScorecard();
235
- }
236
- getStatusSummary() {
237
- const scorecard = this.scorecard;
238
- const successRate = this.calculateSuccessRate(scorecard);
239
- return {
240
- stage: this.getStage(),
241
- successRate,
242
- isInColdStart: this.isColdStart(),
243
- graceRemaining: scorecard.grace_failures_remaining ?? 0,
244
- currentStreak: {
245
- type: scorecard.success_streak > scorecard.failure_streak ? 'success' : 'failure',
246
- count: Math.max(scorecard.success_streak, scorecard.failure_streak)
247
- }
248
- };
249
- }
250
- calculateSuccessRate(scorecard) {
251
- if (!scorecard.history || scorecard.history.length === 0)
252
- return 100;
253
- const recent = scorecard.history.slice(-20);
254
- const successes = recent.filter(h => h.type === 'success').length;
255
- return Math.round((successes / recent.length) * 100);
256
- }
257
- }
258
- export function recordSuccess(workspaceDir, reason, context, isSubagent = false) {
259
- new TrustEngine(workspaceDir).recordSuccess(reason, context, isSubagent);
260
- }
261
- export function recordFailure(type, workspaceDir, ctx) {
262
- new TrustEngine(workspaceDir).recordFailure(type, ctx);
263
- }
264
- export function getAgentScorecard(workspaceDir) {
265
- return new TrustEngine(workspaceDir).getScorecard();
266
- }
267
- export function getTrustStats(scorecard) {
268
- const dummy = new TrustEngine(process.cwd());
269
- const successRate = dummy.calculateSuccessRate(scorecard);
270
- const stages = { stage_1_observer: 30, stage_2_editor: 60, stage_3_developer: 80 };
271
- let stage = scorecard.trust_score < stages.stage_1_observer ? 1 :
272
- scorecard.trust_score < stages.stage_2_editor ? 2 :
273
- scorecard.trust_score < stages.stage_3_developer ? 3 : 4;
274
- return {
275
- stage, successRate,
276
- isInColdStart: scorecard.cold_start_end ? new Date() < new Date(scorecard.cold_start_end) : false,
277
- graceRemaining: scorecard.grace_failures_remaining ?? 0,
278
- currentStreak: {
279
- type: (scorecard.success_streak || 0) > (scorecard.failure_streak || 0) ? 'success' : 'failure',
280
- count: Math.max(scorecard.success_streak || 0, scorecard.failure_streak || 0)
281
- }
282
- };
283
- }
284
- export function getTrustStatus(workspaceDir) {
285
- return new TrustEngine(workspaceDir).getStatusSummary();
286
- }