principles-disciple 1.7.6 → 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 (106) hide show
  1. package/dist/commands/context.js +5 -15
  2. package/dist/commands/evolution-status.js +2 -9
  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/constants/tools.d.ts +2 -2
  12. package/dist/constants/tools.js +1 -1
  13. package/dist/core/adaptive-thresholds.d.ts +186 -0
  14. package/dist/core/adaptive-thresholds.js +300 -0
  15. package/dist/core/config.d.ts +2 -38
  16. package/dist/core/config.js +6 -61
  17. package/dist/core/event-log.d.ts +1 -2
  18. package/dist/core/event-log.js +0 -3
  19. package/dist/core/evolution-engine.js +1 -21
  20. package/dist/core/evolution-reducer.d.ts +7 -1
  21. package/dist/core/evolution-reducer.js +56 -4
  22. package/dist/core/evolution-types.d.ts +61 -9
  23. package/dist/core/evolution-types.js +31 -9
  24. package/dist/core/external-training-contract.d.ts +276 -0
  25. package/dist/core/external-training-contract.js +269 -0
  26. package/dist/core/local-worker-routing.d.ts +175 -0
  27. package/dist/core/local-worker-routing.js +525 -0
  28. package/dist/core/model-deployment-registry.d.ts +218 -0
  29. package/dist/core/model-deployment-registry.js +503 -0
  30. package/dist/core/model-training-registry.d.ts +295 -0
  31. package/dist/core/model-training-registry.js +475 -0
  32. package/dist/core/nocturnal-arbiter.d.ts +159 -0
  33. package/dist/core/nocturnal-arbiter.js +534 -0
  34. package/dist/core/nocturnal-candidate-scoring.d.ts +137 -0
  35. package/dist/core/nocturnal-candidate-scoring.js +266 -0
  36. package/dist/core/nocturnal-compliance.d.ts +175 -0
  37. package/dist/core/nocturnal-compliance.js +824 -0
  38. package/dist/core/nocturnal-dataset.d.ts +224 -0
  39. package/dist/core/nocturnal-dataset.js +443 -0
  40. package/dist/core/nocturnal-executability.d.ts +85 -0
  41. package/dist/core/nocturnal-executability.js +331 -0
  42. package/dist/core/nocturnal-export.d.ts +124 -0
  43. package/dist/core/nocturnal-export.js +275 -0
  44. package/dist/core/nocturnal-paths.d.ts +124 -0
  45. package/dist/core/nocturnal-paths.js +214 -0
  46. package/dist/core/nocturnal-trajectory-extractor.d.ts +242 -0
  47. package/dist/core/nocturnal-trajectory-extractor.js +307 -0
  48. package/dist/core/nocturnal-trinity.d.ts +311 -0
  49. package/dist/core/nocturnal-trinity.js +880 -0
  50. package/dist/core/paths.d.ts +6 -0
  51. package/dist/core/paths.js +6 -0
  52. package/dist/core/principle-training-state.d.ts +121 -0
  53. package/dist/core/principle-training-state.js +321 -0
  54. package/dist/core/promotion-gate.d.ts +238 -0
  55. package/dist/core/promotion-gate.js +529 -0
  56. package/dist/core/session-tracker.d.ts +10 -0
  57. package/dist/core/session-tracker.js +14 -0
  58. package/dist/core/shadow-observation-registry.d.ts +217 -0
  59. package/dist/core/shadow-observation-registry.js +308 -0
  60. package/dist/core/training-program.d.ts +233 -0
  61. package/dist/core/training-program.js +433 -0
  62. package/dist/core/trajectory.d.ts +95 -1
  63. package/dist/core/trajectory.js +220 -6
  64. package/dist/core/workspace-context.d.ts +0 -6
  65. package/dist/core/workspace-context.js +0 -12
  66. package/dist/hooks/bash-risk.d.ts +6 -6
  67. package/dist/hooks/bash-risk.js +8 -8
  68. package/dist/hooks/gate-block-helper.js +1 -1
  69. package/dist/hooks/gate.d.ts +1 -1
  70. package/dist/hooks/gate.js +2 -2
  71. package/dist/hooks/gfi-gate.d.ts +3 -3
  72. package/dist/hooks/gfi-gate.js +15 -14
  73. package/dist/hooks/pain.js +6 -9
  74. package/dist/hooks/progressive-trust-gate.d.ts +21 -49
  75. package/dist/hooks/progressive-trust-gate.js +51 -204
  76. package/dist/hooks/prompt.d.ts +11 -11
  77. package/dist/hooks/prompt.js +158 -72
  78. package/dist/hooks/subagent.js +43 -6
  79. package/dist/i18n/commands.js +8 -8
  80. package/dist/index.js +129 -28
  81. package/dist/service/evolution-worker.d.ts +42 -4
  82. package/dist/service/evolution-worker.js +321 -13
  83. package/dist/service/nocturnal-runtime.d.ts +183 -0
  84. package/dist/service/nocturnal-runtime.js +352 -0
  85. package/dist/service/nocturnal-service.d.ts +163 -0
  86. package/dist/service/nocturnal-service.js +787 -0
  87. package/dist/service/nocturnal-target-selector.d.ts +145 -0
  88. package/dist/service/nocturnal-target-selector.js +315 -0
  89. package/dist/service/phase3-input-filter.d.ts +2 -23
  90. package/dist/service/phase3-input-filter.js +3 -27
  91. package/dist/service/runtime-summary-service.d.ts +0 -10
  92. package/dist/service/runtime-summary-service.js +1 -54
  93. package/dist/tools/deep-reflect.js +2 -1
  94. package/dist/types/event-types.d.ts +2 -10
  95. package/dist/types/runtime-summary.d.ts +1 -8
  96. package/dist/types.d.ts +0 -3
  97. package/dist/types.js +0 -2
  98. package/openclaw.plugin.json +1 -1
  99. package/package.json +1 -1
  100. package/templates/langs/en/skills/pd-mentor/SKILL.md +5 -5
  101. package/templates/langs/zh/skills/pd-mentor/SKILL.md +5 -5
  102. package/templates/pain_settings.json +0 -6
  103. package/dist/commands/trust.d.ts +0 -4
  104. package/dist/commands/trust.js +0 -78
  105. package/dist/core/trust-engine.d.ts +0 -96
  106. package/dist/core/trust-engine.js +0 -286
@@ -4,7 +4,7 @@
4
4
  * Handles Fatigue Index (GFI) based tool blocking with TIER 0-3 classification.
5
5
  *
6
6
  * **Responsibilities:**
7
- * - Calculate dynamic GFI thresholds based on trust stage and line changes
7
+ * - Calculate dynamic GFI thresholds based on EP tier and line changes
8
8
  * - Apply tier-based tool blocking:
9
9
  * - TIER 0: Read-only tools (never blocked)
10
10
  * - TIER 1: Low-risk writes (blocked when GFI >= low_risk_block threshold)
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * **Configuration:**
16
16
  * - GFI thresholds from config.gfi_gate
17
- * - Trust stage multipliers for dynamic threshold calculation
17
+ * - EP tier multipliers for dynamic threshold calculation
18
18
  * - Large change adjustments
19
19
  *
20
20
  * **Block Persistence:**
@@ -27,6 +27,7 @@ import { analyzeBashCommand, calculateDynamicThreshold } from './bash-risk.js';
27
27
  import { BASH_TOOLS_SET, HIGH_RISK_TOOLS, LOW_RISK_WRITE_TOOLS, AGENT_TOOLS } from '../constants/tools.js';
28
28
  import { AGENT_SPAWN_GFI_THRESHOLD } from '../config/index.js';
29
29
  import { recordGateBlockAndReturn } from './gate-block-helper.js';
30
+ import { getEvolutionEngine } from '../core/evolution-engine.js';
30
31
  /**
31
32
  * Internal helper to call the shared block helper with gfi-gate source tag.
32
33
  */
@@ -45,6 +46,9 @@ export function checkGfiGate(event, wctx, sessionId, config, logger) {
45
46
  }
46
47
  const session = getSession(sessionId);
47
48
  const currentGfi = session?.currentGfi || 0;
49
+ const getEpTier = () => {
50
+ return getEvolutionEngine(wctx.workspaceDir).getTier();
51
+ };
48
52
  // TIER 3: Bash commands
49
53
  if (BASH_TOOLS_SET.has(event.toolName)) {
50
54
  const command = String(event.params.command || event.params.args || '');
@@ -57,12 +61,11 @@ export function checkGfiGate(event, wctx, sessionId, config, logger) {
57
61
  return undefined;
58
62
  }
59
63
  // normal bash - check GFI threshold
60
- const trustEngine = wctx.trust;
61
- const stage = trustEngine.getStage();
64
+ const tier = getEpTier();
62
65
  const baseThreshold = config.thresholds?.low_risk_block || 70;
63
- const dynamicThreshold = calculateDynamicThreshold(baseThreshold, stage, 0, {
66
+ const dynamicThreshold = calculateDynamicThreshold(baseThreshold, tier, 0, {
64
67
  large_change_lines: config.large_change_lines || 50,
65
- trust_stage_multipliers: config.trust_stage_multipliers || { '1': 0.5, '2': 0.75, '3': 1.0, '4': 1.5 },
68
+ ep_tier_multipliers: config.ep_tier_multipliers || { '1': 0.5, '2': 0.75, '3': 1.0, '4': 1.5, '5': 2.0 },
66
69
  });
67
70
  if (currentGfi >= dynamicThreshold) {
68
71
  logger?.warn?.(`[PD:GFI_GATE] Bash blocked by GFI: ${currentGfi} >= ${dynamicThreshold}`);
@@ -72,12 +75,11 @@ export function checkGfiGate(event, wctx, sessionId, config, logger) {
72
75
  }
73
76
  // TIER 2: High-risk tools
74
77
  if (HIGH_RISK_TOOLS.has(event.toolName)) {
75
- const trustEngine = wctx.trust;
76
- const stage = trustEngine.getStage();
78
+ const tier = getEpTier();
77
79
  const baseThreshold = config.thresholds?.high_risk_block || 40;
78
- const dynamicThreshold = calculateDynamicThreshold(baseThreshold, stage, 0, {
80
+ const dynamicThreshold = calculateDynamicThreshold(baseThreshold, tier, 0, {
79
81
  large_change_lines: config.large_change_lines || 50,
80
- trust_stage_multipliers: config.trust_stage_multipliers || { '1': 0.5, '2': 0.75, '3': 1.0, '4': 1.5 },
82
+ ep_tier_multipliers: config.ep_tier_multipliers || { '1': 0.5, '2': 0.75, '3': 1.0, '4': 1.5, '5': 2.0 },
81
83
  });
82
84
  if (currentGfi >= dynamicThreshold) {
83
85
  const filePath = event.params.file_path || event.params.path || event.params.file || event.params.target || 'unknown';
@@ -87,13 +89,12 @@ export function checkGfiGate(event, wctx, sessionId, config, logger) {
87
89
  }
88
90
  // TIER 1: Low-risk write tools
89
91
  if (LOW_RISK_WRITE_TOOLS.has(event.toolName)) {
90
- const trustEngine = wctx.trust;
91
- const stage = trustEngine.getStage();
92
+ const tier = getEpTier();
92
93
  const lineChanges = estimateLineChanges({ toolName: event.toolName, params: event.params });
93
94
  const baseThreshold = config.thresholds?.low_risk_block || 70;
94
- const dynamicThreshold = calculateDynamicThreshold(baseThreshold, stage, lineChanges, {
95
+ const dynamicThreshold = calculateDynamicThreshold(baseThreshold, tier, lineChanges, {
95
96
  large_change_lines: config.large_change_lines || 50,
96
- trust_stage_multipliers: config.trust_stage_multipliers || { '1': 0.5, '2': 0.75, '3': 1.0, '4': 1.5 },
97
+ ep_tier_multipliers: config.ep_tier_multipliers || { '1': 0.5, '2': 0.75, '3': 1.0, '4': 1.5, '5': 2.0 },
97
98
  });
98
99
  if (currentGfi >= dynamicThreshold) {
99
100
  const filePath = event.params.file_path || event.params.path || event.params.file || event.params.target || 'unknown';
@@ -7,6 +7,7 @@ import { denoiseError, computeHash } from '../utils/hashing.js';
7
7
  import { SystemLogger } from '../core/system-logger.js';
8
8
  import { WorkspaceContext } from '../core/workspace-context.js';
9
9
  import { getEvolutionLogger, createTraceId } from '../core/evolution-logger.js';
10
+ import { recordEvolutionSuccess, recordEvolutionFailure } from '../core/evolution-engine.js';
10
11
  const WRITE_TOOLS = ['write', 'edit', 'apply_patch', 'write_file', 'edit_file', 'replace'];
11
12
  function shouldAttributePrincipleToTool(principle, toolName) {
12
13
  return principle.contextTags.includes(toolName) || principle.trigger.includes(toolName);
@@ -30,7 +31,6 @@ export function handleAfterToolCall(event, ctx, api) {
30
31
  const wctx = WorkspaceContext.fromHookContext({ ...ctx, workspaceDir: effectiveWorkspaceDir });
31
32
  const config = wctx.config;
32
33
  const eventLog = wctx.eventLog;
33
- const trust = wctx.trust;
34
34
  const sessionId = ctx.sessionId || 'unknown';
35
35
  const sessionState = ctx.sessionId ? getSession(ctx.sessionId) : undefined;
36
36
  const gfiBefore = sessionState?.currentGfi ?? 0;
@@ -106,11 +106,10 @@ export function handleAfterToolCall(event, ctx, api) {
106
106
  }
107
107
  }
108
108
  const isRisk = isRisky(relPath, profile.risk_paths);
109
- trust.recordFailure(isRisk ? 'risky' : 'tool', {
109
+ recordEvolutionFailure(effectiveWorkspaceDir, event.toolName, {
110
+ filePath: relPath,
111
+ reason: isRisk ? 'risky' : 'tool',
110
112
  sessionId,
111
- api,
112
- toolName: event.toolName,
113
- error: event.error // Pass error for timeout detection
114
113
  });
115
114
  // Record tool call failure event
116
115
  eventLog.recordToolCall(sessionId, {
@@ -147,11 +146,9 @@ export function handleAfterToolCall(event, ctx, api) {
147
146
  else {
148
147
  // ── SUCCESS BRANCH ──
149
148
  const resetState = resetFriction(sessionId, effectiveWorkspaceDir);
150
- // 👈 Record success to reset failure streak and earn minor trust (if constructive)
151
- trust.recordSuccess('tool_success', {
149
+ recordEvolutionSuccess(effectiveWorkspaceDir, event.toolName, {
152
150
  sessionId,
153
- api,
154
- toolName: event.toolName // 👈 NEW: Pass toolName for classification
151
+ reason: 'tool_success',
155
152
  });
156
153
  const injectedProbationIds = getInjectedProbationIds(sessionId, effectiveWorkspaceDir);
157
154
  for (const id of injectedProbationIds) {
@@ -1,66 +1,42 @@
1
1
  /**
2
- * Progressive Trust Gate Module
2
+ * Progressive Gate Module (EP-Only Version)
3
3
  *
4
- * Handles progressive access control based on trust stages (1-4).
4
+ * EP (Evolution Points) 是唯一的门控机制。
5
5
  *
6
- * **Responsibilities:**
7
- * - Enforce trust stage-based permissions:
8
- * - Stage 1 (Bankruptcy): Block ALL writes to risk paths, medium+ changes
9
- * - Stage 2 (Editor): Block risk paths, large changes
10
- * - Stage 3 (Developer): Require READY plan for risk paths, normal limits
11
- * - Stage 4 (Architect): Full bypass with audit logging
12
- * - Apply percentage-based line change limits for large files
13
- * - Handle plan approval whitelist for Stage 1
14
- * - Record EP simulation for comparison analysis
6
+ * **EP 门控逻辑:**
7
+ * - Seed (0分): 只读 + 基础文档
8
+ * - Sprout (50分): 单文件编辑
9
+ * - Sapling (200分): 多文件 + 测试 + 子智能体
10
+ * - Tree (500分): 重构 + 风险路径
11
+ * - Forest (1000分): 完全自主
15
12
  *
16
- * **Configuration:**
17
- * - Progressive gate settings from profile.progressive_gate
18
- * - Trust limits from config.trust
19
- * - Plan approval patterns and operations
13
+ * **风险路径控制:**
14
+ * - 低等级不能修改风险路径
15
+ * - 高等级解锁风险路径权限
20
16
  *
21
- * **Block Persistence:**
22
- * - Uses shared `recordGateBlockAndReturn` from gate-block-helper.ts
23
- * - Ensures single authoritative block persistence path
17
+ * **不再有:**
18
+ * - Trust Score (30-100) 系统
19
+ * - Stage 1-4 分级
20
+ * - Plan Approval 白名单机制
21
+ * - 基于行数的限制
24
22
  */
25
23
  import type { PluginHookBeforeToolCallEvent, PluginHookBeforeToolCallResult } from '../openclaw-sdk.js';
26
24
  import type { WorkspaceContext } from '../core/workspace-context.js';
27
25
  /**
28
- * Configuration for progressive gate behavior
26
+ * Build EP gate rejection reason
29
27
  */
30
- export interface ProgressiveGateConfig {
31
- enabled?: boolean;
32
- plan_approvals?: {
33
- enabled?: boolean;
34
- max_lines_override?: number;
35
- allowed_patterns?: string[];
36
- allowed_operations?: string[];
37
- };
38
- }
28
+ export declare function buildEvolutionGateReason(tier: number, tierName: string, reason: string): string;
39
29
  /**
40
- * Trust stage limits configuration
41
- */
42
- export interface TrustLimits {
43
- stage_2_max_lines?: number;
44
- stage_3_max_lines?: number;
45
- stage_2_max_percentage?: number;
46
- stage_3_max_percentage?: number;
47
- min_lines_fallback?: number;
48
- }
49
- /**
50
- * Build line limit rejection reason
51
- */
52
- export declare function buildLineLimitReason(lineChanges: number, effectiveLimit: number, limitType: 'percentage' | 'fixed', targetLineCount: number | null, actualPercentage: number | null, stage: number): string;
53
- /**
54
- * Check progressive trust gate based on trust stage and operation context
30
+ * Check EP-based gate
55
31
  *
56
32
  * @param event - The tool call event
57
33
  * @param wctx - Workspace context
58
34
  * @param relPath - Relative path to target file
59
35
  * @param risky - Whether the path is a risk path
60
- * @param lineChanges - Estimated line changes
36
+ * @param lineChanges - Estimated line changes (kept for interface compatibility, not used for gating)
61
37
  * @param logger - Logger instance
62
38
  * @param ctx - Hook context
63
- * @param profile - Gate profile containing risk_paths and progressive_gate config
39
+ * @param profile - Gate profile containing risk_paths config
64
40
  * @returns PluginHookBeforeToolCallResult to block, or undefined to allow
65
41
  */
66
42
  export declare function checkProgressiveTrustGate(event: PluginHookBeforeToolCallEvent, wctx: WorkspaceContext, relPath: string, risky: boolean, lineChanges: number, logger: {
@@ -72,8 +48,4 @@ export declare function checkProgressiveTrustGate(event: PluginHookBeforeToolCal
72
48
  sessionId?: string;
73
49
  }, profile?: {
74
50
  risk_paths: string[];
75
- progressive_gate?: {
76
- enabled?: boolean;
77
- plan_approvals?: any;
78
- };
79
51
  }): PluginHookBeforeToolCallResult | void;
@@ -1,49 +1,32 @@
1
1
  /**
2
- * Progressive Trust Gate Module
2
+ * Progressive Gate Module (EP-Only Version)
3
3
  *
4
- * Handles progressive access control based on trust stages (1-4).
4
+ * EP (Evolution Points) 是唯一的门控机制。
5
5
  *
6
- * **Responsibilities:**
7
- * - Enforce trust stage-based permissions:
8
- * - Stage 1 (Bankruptcy): Block ALL writes to risk paths, medium+ changes
9
- * - Stage 2 (Editor): Block risk paths, large changes
10
- * - Stage 3 (Developer): Require READY plan for risk paths, normal limits
11
- * - Stage 4 (Architect): Full bypass with audit logging
12
- * - Apply percentage-based line change limits for large files
13
- * - Handle plan approval whitelist for Stage 1
14
- * - Record EP simulation for comparison analysis
6
+ * **EP 门控逻辑:**
7
+ * - Seed (0分): 只读 + 基础文档
8
+ * - Sprout (50分): 单文件编辑
9
+ * - Sapling (200分): 多文件 + 测试 + 子智能体
10
+ * - Tree (500分): 重构 + 风险路径
11
+ * - Forest (1000分): 完全自主
15
12
  *
16
- * **Configuration:**
17
- * - Progressive gate settings from profile.progressive_gate
18
- * - Trust limits from config.trust
19
- * - Plan approval patterns and operations
13
+ * **风险路径控制:**
14
+ * - 低等级不能修改风险路径
15
+ * - 高等级解锁风险路径权限
20
16
  *
21
- * **Block Persistence:**
22
- * - Uses shared `recordGateBlockAndReturn` from gate-block-helper.ts
23
- * - Ensures single authoritative block persistence path
17
+ * **不再有:**
18
+ * - Trust Score (30-100) 系统
19
+ * - Stage 1-4 分级
20
+ * - Plan Approval 白名单机制
21
+ * - 基于行数的限制
24
22
  */
25
- import * as fs from 'fs';
26
- import * as path from 'path';
27
- import { planStatus as getPlanStatus } from '../utils/io.js';
28
- import { matchesAnyPattern } from '../utils/glob-match.js';
29
- import { assessRiskLevel, getTargetFileLineCount, calculatePercentageThreshold } from '../core/risk-calculator.js';
30
23
  import { checkEvolutionGate } from '../core/evolution-engine.js';
31
- import { EventLogService } from '../core/event-log.js';
32
24
  import { recordGateBlockAndReturn } from './gate-block-helper.js';
33
25
  /**
34
- * Build line limit rejection reason
26
+ * Build EP gate rejection reason
35
27
  */
36
- export function buildLineLimitReason(lineChanges, effectiveLimit, limitType, targetLineCount, actualPercentage, stage) {
37
- if (limitType === 'percentage' && targetLineCount !== null && actualPercentage !== null) {
38
- return `Modification too large: ${lineChanges} lines (${actualPercentage}% of ${targetLineCount} lines). ` +
39
- `Stage ${stage} limit is ${effectiveLimit} lines (${limitType}). ` +
40
- `Threshold calculation: min(${targetLineCount} × ${actualPercentage}%, ${effectiveLimit} lines).`;
41
- }
42
- else {
43
- return `Modification too large: ${lineChanges} lines. ` +
44
- `Stage ${stage} limit is ${effectiveLimit} lines (fixed threshold). ` +
45
- `Note: Could not read target file to calculate percentage-based limit. Check file permissions and encoding.`;
46
- }
28
+ export function buildEvolutionGateReason(tier, tierName, reason) {
29
+ return `[EP Gate] Tier ${tier} (${tierName}): ${reason}`;
47
30
  }
48
31
  /**
49
32
  * Internal helper to call the shared block helper with progressive-trust-gate source tag.
@@ -58,185 +41,49 @@ function block(filePath, reason, wctx, toolName, logger, sessionId) {
58
41
  }, logger);
59
42
  }
60
43
  /**
61
- * Check progressive trust gate based on trust stage and operation context
44
+ * Check EP-based gate
62
45
  *
63
46
  * @param event - The tool call event
64
47
  * @param wctx - Workspace context
65
48
  * @param relPath - Relative path to target file
66
49
  * @param risky - Whether the path is a risk path
67
- * @param lineChanges - Estimated line changes
50
+ * @param lineChanges - Estimated line changes (kept for interface compatibility, not used for gating)
68
51
  * @param logger - Logger instance
69
52
  * @param ctx - Hook context
70
- * @param profile - Gate profile containing risk_paths and progressive_gate config
53
+ * @param profile - Gate profile containing risk_paths config
71
54
  * @returns PluginHookBeforeToolCallResult to block, or undefined to allow
72
55
  */
73
56
  export function checkProgressiveTrustGate(event, wctx, relPath, risky, lineChanges, logger, ctx, profile) {
74
- // Check if progressive gate is disabled
75
- if (profile?.progressive_gate?.enabled === false) {
57
+ // EP is the only gate now - use actual gate decision
58
+ if (!ctx.workspaceDir) {
59
+ logger.warn?.('[PD_GATE] No workspaceDir, skipping EP gate check');
76
60
  return;
77
61
  }
78
- const trustEngine = wctx.trust;
79
- const trustScore = trustEngine.getScore();
80
- const stage = trustEngine.getStage();
81
- const trustSettings = wctx.config.get('trust') || {
82
- limits: {
83
- stage_2_max_lines: 50,
84
- stage_3_max_lines: 300,
85
- stage_2_max_percentage: 10,
86
- stage_3_max_percentage: 15,
87
- min_lines_fallback: 20,
88
- }
89
- };
90
- const riskLevel = assessRiskLevel(relPath, { toolName: event.toolName, params: event.params }, profile?.risk_paths || []);
91
- const planApprovals = profile?.progressive_gate?.plan_approvals;
92
- const canUsePlanApproval = Boolean(stage === 1 &&
93
- planApprovals?.enabled &&
94
- getPlanStatus(ctx.workspaceDir || '') === 'READY' &&
95
- planApprovals.allowed_operations?.includes(event.toolName) &&
96
- matchesAnyPattern(relPath, planApprovals.allowed_patterns || []) &&
97
- ((planApprovals.max_lines_override ?? -1) === -1 || lineChanges <= (planApprovals.max_lines_override ?? -1)));
98
- logger.info?.(`[PD_GATE] Trust: ${trustScore} (Stage ${stage}), Risk: ${riskLevel}, Path: ${relPath}`);
99
- // ── EP SIMULATION MODE (M6验证) ──
100
- // 记录EP系统的模拟决策,但不生效(仅用于对比分析)
101
- // BUGFIX #90: 移到所有Stage检查之前,确保所有Stage都触发EP simulation记录
102
- try {
103
- const epDecision = checkEvolutionGate(ctx.workspaceDir, {
104
- toolName: event.toolName,
105
- content: event.params?.content,
106
- lineCount: lineChanges,
107
- isRiskPath: risky,
108
- });
109
- const epLogEntry = {
110
- timestamp: new Date().toISOString(),
111
- toolName: event.toolName,
112
- filePath: relPath,
113
- trustEngine: { score: trustScore, stage, decision: 'allow' },
114
- epSystem: { tier: epDecision.currentTier ?? 'UNKNOWN', allowed: epDecision.allowed, reason: epDecision.reason },
115
- conflict: epDecision.allowed === false, // Trust允许但EP拒绝(任何阶段)
116
- };
117
- const epLogPath = path.join(ctx.workspaceDir, '.state', 'ep_simulation.jsonl');
118
- // 安全创建目录(如果失败则跳过日志写入,但不影响 Trust Engine 决策)
119
- let canWriteEpLog = true;
120
- try {
121
- fs.mkdirSync(path.dirname(epLogPath), { recursive: true });
122
- }
123
- catch (mkdirErr) {
124
- if (!mkdirErr || mkdirErr.code !== 'EEXIST') {
125
- logger.warn?.(`[PD_EP_SIM] Failed to create log dir: ${mkdirErr?.message ?? String(mkdirErr)}, skipping log`);
126
- canWriteEpLog = false;
127
- }
128
- }
129
- if (canWriteEpLog) {
130
- fs.appendFileSync(epLogPath, JSON.stringify(epLogEntry) + '\n');
131
- }
132
- logger.info?.(`[PD_EP_SIM] Tier: ${epDecision.currentTier}, Allowed: ${epDecision.allowed}, Trust: ${trustScore} (Stage ${stage})`);
133
- }
134
- catch (err) {
135
- // EP 模拟失败不应该影响 Trust Engine 决策
136
- const errMsg = err instanceof Error ? err.message : String(err);
137
- logger.warn?.(`[PD_EP_SIM] Simulation failed: ${errMsg}, continuing with Trust Engine`);
138
- }
139
- // Stage 1 (Bankruptcy): Block ALL writes to risk paths, and all medium+ writes
140
- if (stage === 1) {
141
- if (canUsePlanApproval) {
142
- const planStatus = 'READY';
143
- wctx.eventLog.recordPlanApproval(ctx.sessionId, {
144
- toolName: event.toolName,
145
- filePath: relPath,
146
- pattern: relPath,
147
- planStatus
148
- });
149
- wctx.trajectory?.recordGateBlock?.({
150
- sessionId: ctx.sessionId,
151
- toolName: event.toolName,
152
- filePath: relPath,
153
- reason: 'plan_approval',
154
- planStatus,
155
- });
156
- logger.info?.(`[PD_GATE] Stage 1 PLAN approval: ${relPath}`);
157
- return;
158
- }
159
- if (risky || riskLevel !== 'LOW') {
160
- // Block if not approved by whitelist
161
- return block(relPath, `Trust score too low (${trustScore}). Stage 1 agents cannot modify risk paths or perform non-trivial edits.`, wctx, event.toolName, logger, ctx.sessionId);
162
- }
163
- }
164
- // Stage 2 (Editor): Block writes to risk paths. Block large changes.
165
- if (stage === 2) {
166
- if (risky) {
167
- return block(relPath, `Stage 2 agents are not authorized to modify risk paths.`, wctx, event.toolName, logger, ctx.sessionId);
168
- }
169
- // Percentage-based threshold calculation
170
- const targetAbsolutePath = typeof event.params.file_path === 'string' && ctx.workspaceDir ? path.join(ctx.workspaceDir, event.params.file_path) : null;
171
- const targetLineCount = targetAbsolutePath ? getTargetFileLineCount(targetAbsolutePath) : null;
172
- const minLinesFallback = trustSettings.limits?.min_lines_fallback ?? 20;
173
- const stage2MaxPercentage = trustSettings.limits?.stage_2_max_percentage ?? 10;
174
- const stage2FixedLimit = trustSettings.limits?.stage_2_max_lines ?? 50;
175
- let effectiveLimit;
176
- let limitType;
177
- let actualPercentage = null;
178
- if (targetLineCount !== null && targetLineCount > 0) {
179
- effectiveLimit = calculatePercentageThreshold(targetLineCount, stage2MaxPercentage, minLinesFallback);
180
- actualPercentage = Math.round((lineChanges / targetLineCount) * 100);
181
- limitType = 'percentage';
182
- }
183
- else {
184
- effectiveLimit = stage2FixedLimit;
185
- limitType = 'fixed';
186
- }
187
- if (lineChanges > effectiveLimit) {
188
- return block(relPath, buildLineLimitReason(lineChanges, effectiveLimit, limitType, targetLineCount, actualPercentage, 2), wctx, event.toolName, logger, ctx.sessionId);
189
- }
190
- }
191
- // Stage 3 (Developer): Allow normal writes. Require READY plan for risk paths.
192
- if (stage === 3) {
193
- if (risky) {
194
- const planStatus = getPlanStatus(ctx.workspaceDir || '');
195
- if (planStatus !== 'READY') {
196
- return block(relPath, `No READY plan found. Stage 3 requires a plan for risk path modifications.`, wctx, event.toolName, logger, ctx.sessionId);
197
- }
198
- }
199
- // Percentage-based threshold calculation
200
- const targetAbsolutePath = typeof event.params.file_path === 'string' && ctx.workspaceDir ? path.join(ctx.workspaceDir, event.params.file_path) : null;
201
- const targetLineCount = targetAbsolutePath ? getTargetFileLineCount(targetAbsolutePath) : null;
202
- const minLinesFallback = trustSettings.limits?.min_lines_fallback ?? 20;
203
- const stage3MaxPercentage = trustSettings.limits?.stage_3_max_percentage ?? 15;
204
- const stage3FixedLimit = trustSettings.limits?.stage_3_max_lines ?? 300;
205
- let effectiveLimit;
206
- let limitType;
207
- let actualPercentage = null;
208
- if (targetLineCount !== null && targetLineCount > 0) {
209
- effectiveLimit = calculatePercentageThreshold(targetLineCount, stage3MaxPercentage, minLinesFallback);
210
- actualPercentage = Math.round((lineChanges / targetLineCount) * 100);
211
- limitType = 'percentage';
212
- }
213
- else {
214
- effectiveLimit = stage3FixedLimit;
215
- limitType = 'fixed';
216
- }
217
- if (lineChanges > effectiveLimit) {
218
- return block(relPath, buildLineLimitReason(lineChanges, effectiveLimit, limitType, targetLineCount, actualPercentage, 3), wctx, event.toolName, logger, ctx.sessionId);
219
- }
220
- }
221
- // Stage 4 (Architect): Full bypass
222
- if (stage === 4) {
223
- logger.info?.(`[PD_GATE] Trusted Architect bypass for ${relPath}`);
224
- // Audit log for Stage 4 bypass (security traceability)
225
- try {
226
- const stateDir = wctx.resolve('STATE_DIR');
227
- const eventLog = EventLogService.get(stateDir);
228
- eventLog.recordGateBypass(ctx.sessionId, {
229
- toolName: event.toolName,
230
- filePath: relPath,
231
- bypassType: 'stage4_architect',
232
- trustScore,
233
- trustStage: stage,
234
- });
235
- logger.info?.(`[PD_GATE] Stage 4 Architect bypass recorded for ${relPath}`);
236
- }
237
- catch (auditErr) {
238
- logger.warn?.(`[PD_GATE] Failed to record Stage 4 bypass audit: ${String(auditErr)}`);
239
- }
240
- return;
62
+ // Call EP gate - this is the actual gate, not simulation
63
+ const epDecision = checkEvolutionGate(ctx.workspaceDir, {
64
+ toolName: event.toolName,
65
+ isRiskPath: risky,
66
+ });
67
+ const currentTier = epDecision.currentTier ?? 1;
68
+ const tierName = getTierName(currentTier);
69
+ logger.info?.(`[PD_GATE] EP Gate: Tier ${currentTier} (${tierName}), Tool: ${event.toolName}, Risk: ${risky}, Allowed: ${epDecision.allowed}`);
70
+ if (!epDecision.allowed) {
71
+ const reason = buildEvolutionGateReason(currentTier, tierName, epDecision.reason ?? 'Unknown restriction');
72
+ return block(relPath, reason, wctx, event.toolName, logger, ctx.sessionId);
241
73
  }
74
+ // Gate passed - allow
75
+ return;
76
+ }
77
+ /**
78
+ * Get tier name from tier number
79
+ */
80
+ function getTierName(tier) {
81
+ const names = {
82
+ 1: 'Seed',
83
+ 2: 'Sprout',
84
+ 3: 'Sapling',
85
+ 4: 'Tree',
86
+ 5: 'Forest',
87
+ };
88
+ return names[tier] ?? 'Unknown';
242
89
  }
@@ -2,7 +2,7 @@ import type { PluginHookBeforePromptBuildEvent, PluginHookAgentContext, PluginHo
2
2
  import { ContextInjectionConfig } from '../types.js';
3
3
  import { type EmpathyObserverApi } from '../service/empathy-observer-manager.js';
4
4
  /**
5
- * 濞寸媴绲块幃濠冾渶濡鍚囬梺鏉跨Ф閻?
5
+ * Default model configuration for OpenClaw agents
6
6
  */
7
7
  interface AgentsDefaultsConfig {
8
8
  model?: unknown;
@@ -23,22 +23,22 @@ interface PromptHookApi {
23
23
  logger: PluginLogger;
24
24
  }
25
25
  /**
26
- * 濞?OpenClaw 闂佹澘绉堕悿鍡樼▔椤撯寬鎺楀几閹邦劷渚€宕圭€n喒鍋撴径瀣仴
27
- * 闁衡偓椤栨稑鐦?string 闁?{ primary, fallbacks } 闁哄秶鍘х槐?
28
- * @internal 閻庣數鍘ч崵顓熺閸涱剛杩旀繛鏉戭儓閻︻垱鎷呯捄銊︽殢
26
+ * Resolves model configuration for OpenClaw agents, supporting string and object formats
27
+ * @param modelConfig - Model config: string (e.g. "provider/model") or { primary, fallbacks } object
28
+ * @internal Helper for model configuration resolution
29
29
  */
30
30
  export declare function resolveModelFromConfig(modelConfig: unknown, logger?: PluginLogger): string | null;
31
31
  /**
32
- * 闁告梻濮惧ù鍥ㄧ▔婵犱胶鐟撻柡鍌氭处閺佺偤宕楅妷鈺佸赋缂?
33
- * 濞?PROFILE.json 閻犲洩顕цぐ?contextInjection 闂佹澘绉堕悿鍡涙晬鐏炵瓔娲ら柡瀣矆缁楀鈧稒锚濠€顏堝礆濞嗘帞绠查柛銉у仱缁垳鎷嬮妶澶婂赋缂?
34
- * @internal 閻庣數鍘ч崵顓熺瑹濞戞ê寰撳ù鐘崇墬鑶╅柛褎銇炴繛鍥偨?
32
+ * Loads context injection config from .principles/PROFILE.json
33
+ * Parses contextInjection configuration from PROFILE.json for context injection
34
+ * @internal Used by evolution engine for context settings
35
35
  */
36
36
  export declare function loadContextInjectionConfig(workspaceDir: string): ContextInjectionConfig;
37
37
  /**
38
- * 闁兼儳鍢茶ぐ鍥╂嫚婵犲啯鐒介悗娑欏姈濞呫倝鎳楅幋鎺旂Ъ閹煎瓨鏌ф繛鍥偨閵娧勭暠婵☆垪鈧磭鈧?
39
- * 濞村吋锚閸樻稓鐥缁辩殜ubagents.model > 濞戞挾绮啯闁?
40
- * 濠碘€冲€归悘澶愭焾閼恒儳姊鹃柡鍫濐樀閸樸倗绱旈鍡欑闁硅埖绋戦崵顓㈡煥濞嗘帩鍤?
41
- * @internal 閻庣數鍘ч崵顓熺閸涱剛杩旀繛鏉戭儓閻︻垱鎷呯捄銊︽殢
38
+ * Gets the diagnostician model - the model used for AI self-diagnosis and reflection
39
+ * Priority: subagents.model > subagents.model > env.OPENCLAW_MODEL
40
+ * Falls back to main model if no diagnostician model is configured
41
+ * @internal Helper for model configuration resolution
42
42
  */
43
43
  export declare function getDiagnosticianModel(api: PromptHookApi | null, logger?: PluginLogger): string;
44
44
  export declare function handleBeforePromptBuild(event: PluginHookBeforePromptBuildEvent, ctx: PluginHookAgentContext & {