principles-disciple 1.76.0 → 1.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  "id": "principles-disciple",
3
3
  "name": "Principles Disciple",
4
4
  "description": "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
5
- "version": "1.76.0",
5
+ "version": "1.78.0",
6
6
  "activation": {
7
7
  "onCapabilities": [
8
8
  "hook"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "principles-disciple",
3
- "version": "1.76.0",
3
+ "version": "1.78.0",
4
4
  "description": "Native OpenClaw plugin for Principles Disciple",
5
5
  "type": "module",
6
6
  "main": "./dist/bundle.js",
@@ -226,7 +226,7 @@ function applyPreset(
226
226
  break;
227
227
  case 'standard':
228
228
  config = {
229
- thinkingOs: true,
229
+ thinkingOs: false,
230
230
  projectFocus: 'off',
231
231
  evolutionContext: { ...defaultContextConfig.evolutionContext }
232
232
  };
@@ -272,8 +272,8 @@ function showHelp(isZh: boolean): string {
272
272
 
273
273
  **预设模式**:
274
274
  \`/pd-context minimal\` - 最小模式(仅核心原则)
275
- \`/pd-context standard\` - 标准模式(原则+思维模型)
276
- \`/pd-context full\` - 完整模式(全部开启)
275
+ \`/pd-context standard\` - 标准模式(原则,不含思维模型)
276
+ \`/pd-context full\` - 完整模式(原则+思维模型+项目上下文)
277
277
 
278
278
  **注意**: 核心原则始终注入,不可关闭。
279
279
  `.trim();
@@ -291,8 +291,8 @@ function showHelp(isZh: boolean): string {
291
291
 
292
292
  **Presets**:
293
293
  \`/pd-context minimal\` - Minimal mode (core principles only)
294
- \`/pd-context standard\` - Standard mode (principles + thinking)
295
- \`/pd-context full\` - Full mode (all enabled)
294
+ \`/pd-context standard\` - Standard mode (principles, no Thinking OS)
295
+ \`/pd-context full\` - Full mode (principles + Thinking OS + project context)
296
296
 
297
297
  **Note**: Core Principles are always injected and cannot be disabled.
298
298
  `.trim();
@@ -0,0 +1,130 @@
1
+ import {
2
+ PLUGIN_SURFACE_REGISTRY,
3
+ validateSurfaceRegistry,
4
+ getSurfacesByCategory,
5
+ type PluginSurfaceEntry,
6
+ type MvpCategory,
7
+ } from '@principles/core/runtime-v2';
8
+ import type { OpenClawPluginService } from '../openclaw-sdk.js';
9
+
10
+ export interface SurfaceGuardResult {
11
+ passed: boolean;
12
+ enabledCoreSurfaces: string[];
13
+ disabledNonCoreSurfaces: string[];
14
+ violations: string[];
15
+ warnings: string[];
16
+ }
17
+
18
+ export function checkSurfaceGuard(): SurfaceGuardResult {
19
+ const validation = validateSurfaceRegistry(PLUGIN_SURFACE_REGISTRY);
20
+ const violations: string[] = [];
21
+ const warnings: string[] = [...validation.warnings];
22
+
23
+ const coreSurfaces = getSurfacesByCategory(PLUGIN_SURFACE_REGISTRY, 'core');
24
+ const enabledCore = coreSurfaces.filter(s => s.enabledByDefault);
25
+ const nonCoreEnabled = PLUGIN_SURFACE_REGISTRY.filter(
26
+ s => s.category !== 'core' && s.enabledByDefault,
27
+ );
28
+
29
+ if (nonCoreEnabled.length > 0) {
30
+ for (const surface of nonCoreEnabled) {
31
+ violations.push(
32
+ `non-core surface '${surface.id}' (${surface.category}) is enabledByDefault=true — must be false per ADR-0014`,
33
+ );
34
+ }
35
+ }
36
+
37
+ if (!validation.valid) {
38
+ violations.push(...validation.errors);
39
+ }
40
+
41
+ return {
42
+ passed: violations.length === 0,
43
+ enabledCoreSurfaces: enabledCore.map(s => s.id),
44
+ disabledNonCoreSurfaces: PLUGIN_SURFACE_REGISTRY
45
+ .filter(s => s.category !== 'core' && !s.enabledByDefault)
46
+ .map(s => s.id),
47
+ violations,
48
+ warnings,
49
+ };
50
+ }
51
+
52
+ export function getSurfaceIdForHook(hookEvent: string, label?: string): string {
53
+ if (label) {
54
+ return `hook:${hookEvent}.${label}`;
55
+ }
56
+ return `hook:${hookEvent}`;
57
+ }
58
+
59
+ export function getSurfaceIdForService(serviceName: string): string {
60
+ return `service:${serviceName}`;
61
+ }
62
+
63
+ export function isSurfaceEnabled(
64
+ surfaceId: string,
65
+ overrides: Record<string, boolean> = {},
66
+ ): { enabled: boolean; reason?: string } {
67
+ const entry = PLUGIN_SURFACE_REGISTRY.find(s => s.id === surfaceId);
68
+
69
+ if (!entry) {
70
+ return {
71
+ enabled: false,
72
+ reason: `surface '${surfaceId}' not found in registry — classify before enabling (PRI-289)`,
73
+ };
74
+ }
75
+
76
+ if (Object.hasOwn(overrides, surfaceId)) {
77
+ const override = overrides[surfaceId];
78
+ if (typeof override !== 'boolean') {
79
+ return { enabled: entry.enabledByDefault, reason: `override for '${surfaceId}' is not boolean, using default` };
80
+ }
81
+ if (entry.category === 'gone') {
82
+ return { enabled: false, reason: `surface '${surfaceId}' is gone and cannot be re-enabled` };
83
+ }
84
+ if (entry.category === 'core' && !override) {
85
+ return { enabled: true, reason: `surface '${surfaceId}' is core and cannot be disabled` };
86
+ }
87
+ return { enabled: override };
88
+ }
89
+
90
+ if (!entry.enabledByDefault && entry.disabledReason) {
91
+ return { enabled: false, reason: entry.disabledReason };
92
+ }
93
+
94
+ return { enabled: entry.enabledByDefault };
95
+ }
96
+
97
+ export type HookHandler<E, C, R> = (event: E, ctx: C) => R | Promise<R>;
98
+
99
+ export function guardHook<E, C, R>(
100
+ surfaceId: string,
101
+ logger: { info?: (msg: string) => void; debug?: (msg: string) => void } | undefined,
102
+ handler: HookHandler<E, C, R>,
103
+ ): HookHandler<E, C, R> {
104
+ const check = isSurfaceEnabled(surfaceId);
105
+ if (check.enabled) {
106
+ return handler;
107
+ }
108
+ const reason = check.reason ?? 'surface not enabled';
109
+ return (_event: E, _ctx: C): R | Promise<R> => {
110
+ logger?.info?.(`[PD:surface-guard] SKIP ${surfaceId}: ${reason}`);
111
+ return undefined as R;
112
+ };
113
+ }
114
+
115
+ export function guardService<T extends OpenClawPluginService>(
116
+ surfaceId: string,
117
+ service: T,
118
+ logger?: { info?: (msg: string) => void; debug?: (msg: string) => void },
119
+ ): T | null {
120
+ const check = isSurfaceEnabled(surfaceId);
121
+ if (check.enabled) {
122
+ return service;
123
+ }
124
+ const reason = check.reason ?? 'surface not enabled';
125
+ logger?.info?.(`[PD:surface-guard] SKIP service ${surfaceId}: ${reason}`);
126
+ return null;
127
+ }
128
+
129
+ export { PLUGIN_SURFACE_REGISTRY, validateSurfaceRegistry, getSurfacesByCategory };
130
+ export type { PluginSurfaceEntry, MvpCategory };
@@ -7,7 +7,8 @@ import { clearInjectedProbationIds, getSession, resetFriction, setInjectedProbat
7
7
  import { WorkspaceContext } from '../core/workspace-context.js';
8
8
  import type { ContextInjectionConfig} from '../types.js';
9
9
  import { defaultContextConfig } from '../types.js';
10
- import { classifyTask, type RoutingInput } from '../core/local-worker-routing.js';
10
+ // local-worker-routing: removed from prompt injection per PRI-291 (MVP-Quiet)
11
+ // classifyTask is still available for non-prompt consumers
11
12
  import { extractSummary, getHistoryVersions, parseWorkingMemorySection, workingMemoryToInjection, autoCompressFocus, safeReadCurrentFocus } from '../core/focus-history.js';
12
13
  import { PathResolver } from '../core/path-resolver.js';
13
14
  import { selectPrinciplesForInjection, DEFAULT_PRINCIPLE_BUDGET } from '../core/principle-injection.js';
@@ -25,7 +26,6 @@ import { evaluatePainDiagnosticGate } from '../core/pain-diagnostic-gate.js';
25
26
  import { emitPainDetectedEvent, buildTrajectoryEvidence } from './pain.js';
26
27
  import { CorrectionCueLearner } from '../core/correction-cue-learner.js';
27
28
  import {
28
- buildAttitudeDirective,
29
29
  detectCorrectionCue as coreDetectCorrectionCue,
30
30
  extractMessageContent,
31
31
  isMinimalTrigger,
@@ -703,10 +703,9 @@ ${heartbeatChecklist}
703
703
 
704
704
  }
705
705
 
706
- // ──── 6. Dynamic Attitude Matrix (based on GFI) ────
707
-
708
- const currentGfi = session?.currentGfi || 0;
709
- const attitudeDirective = buildAttitudeDirective(currentGfi);
706
+ // ──── 6. GFI score (for empathy/evidence path only — NOT for attitude/personality prompt)
707
+ // Attitude/personality prompt injection removed per PRI-291 (MVP diet).
708
+ // GFI scoring, trackFriction, and empathy pain emission remain active.
710
709
 
711
710
  // ──── 7. appendSystemContext: Principles + Thinking OS + reflection_log + project_context ────
712
711
  // NOTE: Principles is ALWAYS injected (not configurable)
@@ -1005,109 +1004,9 @@ ${empathySilenceConstraint}
1005
1004
  prependSystemContext += directiveLines.join('\n');
1006
1005
  }
1007
1006
 
1008
- // Routing Guidance (section 5 injected between evolution principles and core principles)
1009
- // Inject delegation guidance when task is bounded + deployment allowed + not high-entropy.
1010
- // This is a non-authoritative suggestion the main agent decides whether to follow.
1011
- // Shadow evidence comes from real runtime hooks (subagent_spawning/subagent_ended).
1012
- if (!isMinimalMode && sessionId) {
1013
- try {
1014
- // Use the already extracted and cleaned user message
1015
- const latestUserText = latestUserMessage || '';
1016
-
1017
- if (latestUserText && latestUserText.trim().length > 0) {
1018
- // Infer requestedTools and requestedFiles from message content
1019
- const toolPatterns: { pattern: RegExp; tool: string }[] = [
1020
- { pattern: /\b(edit|replace|write|modify|update|fix|patch|add|remove|delete|insert)\b/gi, tool: 'edit' },
1021
- { pattern: /\b(read|cat|view|show|get|find|search|grep|look|inspect|examine|list|head|tail|diff)\b/gi, tool: 'read' },
1022
- { pattern: /\b(run|execute|exec|bash|shell|command)\b/gi, tool: 'bash' },
1023
- ];
1024
- const filePattern = /\b([a-zA-Z]:\\?[^\s,]+\.[a-z]{2,10}|[./][^\s,]+\.[a-z]{2,10})\b/gi;
1025
- const toolMatches = toolPatterns.flatMap(({ pattern, tool }) => {
1026
- const matches: string[] = [];
1027
- const r = new RegExp(pattern.source, pattern.flags);
1028
- while (r.exec(latestUserText) !== null) matches.push(tool);
1029
- return matches;
1030
- });
1031
- const fileMatches = latestUserText.match(filePattern) ?? [];
1032
-
1033
- const routingInput: RoutingInput = {
1034
- taskIntent: toolMatches[0] ?? undefined,
1035
- taskDescription: latestUserText.trim(),
1036
- requestedFiles: fileMatches.length > 0 ? fileMatches : undefined,
1037
- };
1038
-
1039
- const decision = classifyTask(routingInput, wctx.stateDir);
1040
-
1041
- // Inject guidance only when: route_local + deployable checkpoint + not high-entropy
1042
- const isDeployableState =
1043
- decision.activeCheckpointState === 'shadow_ready' ||
1044
- decision.activeCheckpointState === 'promotable';
1045
-
1046
- if (
1047
- decision.decision === 'route_local' &&
1048
- decision.targetProfile !== null &&
1049
- isDeployableState
1050
- ) {
1051
- const profile = decision.targetProfile;
1052
-
1053
- if (profile === 'local-reader') {
1054
- appendParts.push(`<routing_guidance>
1055
- DELEGATION SUGGESTION: This task appears suitable for the local-reader subagent.
1056
-
1057
- **Task Fit**: ${decision.reason}
1058
-
1059
- **Suggested Action**: Consider routing to \`local-reader\` (pd-explorer skill) for focused reading, inspection, and information retrieval.
1060
-
1061
- **Why This Works**:
1062
- - Task keywords indicate read-only or inspect operations
1063
- - Bounded scope — no multi-file coordination needed
1064
- - Shadow observation in progress — real runtime evidence being collected
1065
-
1066
- **Note**: This is a non-authoritative suggestion. The main agent decides whether to route based on full context. Shadow evidence from runtime hooks will inform future promotion decisions.
1067
- </routing_guidance>`);
1068
- } else if (profile === 'local-editor') {
1069
- appendParts.push(`<routing_guidance>
1070
- DELEGATION SUGGESTION: This task appears suitable for the local-editor subagent.
1071
-
1072
- **Task Fit**: ${decision.reason}
1073
-
1074
- **Suggested Action**: Consider routing to \`local-editor\` (pd-repair skill) for bounded editing, modification, and repair tasks.
1075
-
1076
- **Why This Works**:
1077
- - Task keywords indicate bounded modification operations
1078
- - Target files appear limited in scope (1-3 files)
1079
- - Shadow observation in progress — real runtime evidence being collected
1080
-
1081
- **Note**: This is a non-authoritative suggestion. The main agent decides whether to route based on full context. Shadow evidence from runtime hooks will inform future promotion decisions.
1082
- </routing_guidance>`);
1083
- }
1084
- } else if (
1085
- decision.decision === 'stay_main' &&
1086
- decision.classification !== 'reader_eligible' &&
1087
- decision.classification !== 'editor_eligible'
1088
- ) {
1089
- // Only show stay_main guidance when the task is genuinely high-entropy/risk/ambiguous
1090
- appendParts.push(`<routing_guidance>
1091
- ROUTING GUIDANCE: Task should remain on the main agent.
1092
-
1093
- **Reason**: ${decision.reason}
1094
-
1095
- **Blockers**: ${decision.blockers.length > 0 ? decision.blockers.join('; ') : 'none'}
1096
-
1097
- **Why Stay Main**:
1098
- - Task contains high-entropy signals (open-ended, multi-step, or ambiguous)
1099
- - Or: task involves risk signals requiring main-agent supervision
1100
- - Or: deployment not available for the natural target profile
1101
-
1102
- **Note**: This is a non-authoritative suggestion backed by policy classification. The main agent has full discretion.
1103
- </routing_guidance>`);
1104
- }
1105
- }
1106
- } catch (e) {
1107
- // Routing guidance is best-effort — never fail the hook
1108
- logger?.warn?.(`[PD:Prompt] Routing guidance injection failed: ${String(e)}`);
1109
- }
1110
- }
1007
+ // Routing guidance removed per PRI-291 (MVP diet).
1008
+ // Local worker routing is MVP-Quiet per ADR-0014 §2.5.
1009
+ // The classifyTask helper and local-worker-routing module are preserved for non-prompt consumers.
1111
1010
 
1112
1011
 
1113
1012
  // 6. Principles (always on, highest priority, goes last for recency effect)
@@ -1130,15 +1029,8 @@ The sections below are ordered by priority. When conflicts arise, **later sectio
1130
1029
  **【EXECUTION RULES】** (Priority: Low → High):
1131
1030
  - \`<behavioral_constraints>\` - Output format restrictions (hide diagnostic JSON)
1132
1031
  - \`<project_context>\` - Current priorities (can be overridden)
1133
- - \`<reflection_log>\` - Past lessons (inform your approach)
1134
- - \`<thinking_os>\` - Thinking models (guide your reasoning)
1135
- - \`<evolution_principles>\` - Newly learned principles (active + probation)
1136
- - \`<routing_guidance>\` - Delegation suggestions (non-authoritative, best-effort)
1032
+ - \`<evolution_principles>\` - Learned principles (active + probation)
1137
1033
  - \`<core_principles>\` - Core rules (NON-NEGOTIABLE, highest priority)
1138
-
1139
- **Remember**: You are the Spicy Evolver. You despise entropy. You evolve through pain.
1140
-
1141
- ${attitudeDirective}
1142
1034
  `;
1143
1035
  }
1144
1036
 
@@ -34,8 +34,8 @@ export const commandDescriptions: Record<string, Record<SupportedLanguage, strin
34
34
  en: 'Research tool upgrade solutions'
35
35
  },
36
36
  'pd-thinking': {
37
- zh: '管理思维模型 [status|propose|audit]',
38
- en: 'Manage Thinking OS [status|propose|audit]'
37
+ zh: '管理思维模型 [status|propose|audit](默认关闭,/pd-context thinking on 开启)',
38
+ en: 'Manage Thinking OS [status|propose|audit] (off by default, enable via /pd-context thinking on)'
39
39
  },
40
40
  'pd-daily': {
41
41
  zh: '配置并发送进化日报',
package/src/index.ts CHANGED
@@ -62,6 +62,7 @@ import { computeRuntimeShadowTaskFingerprint, PD_LOCAL_PROFILES } from './utils/
62
62
  import type { WorkerProfile } from './core/model-deployment-registry.js';
63
63
  import { validateWorkspaceDir } from './core/workspace-dir-validation.js';
64
64
  import { resolveWorkspaceDirFromApi } from './core/path-resolver.js';
65
+ import { checkSurfaceGuard, guardHook, guardService } from './core/surface-guard.js';
65
66
 
66
67
  // Track started workspaces — one-time init + evolution worker per workspace
67
68
  const startedWorkspaces = new Set<string>();
@@ -190,12 +191,25 @@ const plugin = {
190
191
  }, 1000);
191
192
  healthCheckTimer.unref(); // Don't keep process alive for health check
192
193
 
194
+ // ── MVP Surface Guard (PRI-289): Verify surface classification ──
195
+ const surfaceGuard = checkSurfaceGuard();
196
+ if (!surfaceGuard.passed) {
197
+ for (const violation of surfaceGuard.violations) {
198
+ api.logger.error(`[PD:surface-guard] VIOLATION: ${violation}`);
199
+ }
200
+ }
201
+ api.logger.info(`[PD:surface-guard] Core surfaces: ${surfaceGuard.enabledCoreSurfaces.join(', ')}`);
202
+ api.logger.info(`[PD:surface-guard] Disabled non-core surfaces: ${surfaceGuard.disabledNonCoreSurfaces.length}`);
203
+ for (const warning of surfaceGuard.warnings) {
204
+ api.logger.warn(`[PD:surface-guard] ${warning}`);
205
+ }
206
+
193
207
  const language = (api.pluginConfig?.language as string) || 'en';
194
208
 
195
209
  // ── Hook: Prompt Building ──
196
210
  api.on(
197
211
  'before_prompt_build',
198
- async (event: PluginHookBeforePromptBuildEvent, ctx: PluginHookAgentContext): Promise<PluginHookBeforePromptBuildResult | void> => {
212
+ guardHook('hook:before_prompt_build', api.logger, async (event: PluginHookBeforePromptBuildEvent, ctx: PluginHookAgentContext): Promise<PluginHookBeforePromptBuildResult | void> => {
199
213
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'before_prompt_build');
200
214
  if (!workspaceDir) {
201
215
  api.logger.error(
@@ -251,13 +265,13 @@ const plugin = {
251
265
  });
252
266
  api.logger.error(`[PD] Error in before_prompt_build: ${String(err)}`);
253
267
  }
254
- }
268
+ })
255
269
  );
256
270
 
257
271
  // ── Hook: Security Gate ──
258
272
  api.on(
259
273
  'before_tool_call',
260
- (event: PluginHookBeforeToolCallEvent, ctx: PluginHookToolContext): PluginHookBeforeToolCallResult | void => {
274
+ guardHook('hook:before_tool_call', api.logger, (event: PluginHookBeforeToolCallEvent, ctx: PluginHookToolContext): PluginHookBeforeToolCallResult | void => {
261
275
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'before_tool_call');
262
276
  if (!workspaceDir) {
263
277
  api.logger.error(
@@ -286,13 +300,13 @@ const plugin = {
286
300
  }, { flushImmediately: true });
287
301
  api.logger.error(`[PD] Error in before_tool_call: ${String(err)}`);
288
302
  }
289
- }
303
+ })
290
304
  );
291
305
 
292
306
  // ── Hook: Pain & Trust ──
293
307
  api.on(
294
308
  'after_tool_call',
295
- (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext): void => {
309
+ guardHook('hook:after_tool_call', api.logger, (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext): void => {
296
310
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'after_tool_call');
297
311
  if (!workspaceDir) {
298
312
  api.logger.error(
@@ -319,13 +333,13 @@ const plugin = {
319
333
  }, { flushImmediately: true });
320
334
  api.logger.error(`[PD:EmpathyObserver] Error in after_tool_call: ${String(err)}`);
321
335
  }
322
- }
336
+ })
323
337
  );
324
338
 
325
339
  // ── Hook: LLM Analysis ──
326
340
  api.on(
327
341
  'llm_output',
328
- (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext): void => {
342
+ guardHook('hook:llm_output', api.logger, (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext): void => {
329
343
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'llm_output');
330
344
  if (!workspaceDir) {
331
345
  api.logger.error(
@@ -352,14 +366,14 @@ const plugin = {
352
366
  });
353
367
  api.logger.error(`[PD] Error in llm_output: ${String(err)}`);
354
368
  }
355
- }
369
+ })
356
370
  );
357
371
 
358
372
  // ── Hook: Trajectory Collection (Behavior Evolution Phase 0) ──
359
373
  // Note: after_tool_call and llm_output are safe to collect
360
374
  api.on(
361
375
  'after_tool_call',
362
- (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext): void => {
376
+ guardHook('hook:after_tool_call.trajectory', api.logger, (event: PluginHookAfterToolCallEvent, ctx: PluginHookToolContext): void => {
363
377
  try {
364
378
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'trajectory.after_tool_call');
365
379
  if (!workspaceDir) return;
@@ -368,12 +382,12 @@ const plugin = {
368
382
  } catch (_err) {
369
383
  // Non-critical: don't log, just skip
370
384
  }
371
- }
385
+ })
372
386
  );
373
387
 
374
388
  api.on(
375
389
  'llm_output',
376
- (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext): void => {
390
+ guardHook('hook:llm_output.trajectory', api.logger, (event: PluginHookLlmOutputEvent, ctx: PluginHookAgentContext): void => {
377
391
  try {
378
392
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'trajectory.llm_output');
379
393
  if (!workspaceDir) return;
@@ -382,14 +396,14 @@ const plugin = {
382
396
  } catch (_err) {
383
397
  // Non-critical: don't log, just skip
384
398
  }
385
- }
399
+ })
386
400
  );
387
401
 
388
402
  // ── Hook: Subagent Loop Closure ──
389
403
  api.on(
390
404
  'subagent_spawning',
391
405
 
392
- (event: PluginHookSubagentSpawningEvent, _ctx: PluginHookSubagentContext): void | PluginHookSubagentSpawningResult => {
406
+ guardHook('hook:subagent_spawning', api.logger, (event: PluginHookSubagentSpawningEvent, _ctx: PluginHookSubagentContext): void | PluginHookSubagentSpawningResult => {
393
407
  try {
394
408
  // FIX (B): Never fall back to '.' — fail-fast with ERROR log if workspaceDir cannot be resolved.
395
409
  // For subagent hooks, we use event.agentId as the target agent for workspace resolution.
@@ -428,12 +442,12 @@ const plugin = {
428
442
  api.logger.error(`[PD] Error in subagent_spawning shadow routing: ${String(err)}`);
429
443
  return { status: 'ok' }; // Don't block spawn on shadow observation errors
430
444
  }
431
- }
445
+ })
432
446
  );
433
447
 
434
448
  api.on(
435
449
  'subagent_ended',
436
- (event: PluginHookSubagentEndedEvent, ctx: PluginHookSubagentContext): void => {
450
+ guardHook('hook:subagent_ended', api.logger, (event: PluginHookSubagentEndedEvent, ctx: PluginHookSubagentContext): void => {
437
451
  try {
438
452
  // FIX (B): Never fall back to '.' — fail-fast with ERROR log if workspaceDir cannot be resolved.
439
453
  const workspaceDir = resolveWorkspaceDirFromApi(api, undefined);
@@ -465,11 +479,11 @@ const plugin = {
465
479
  } catch (err) {
466
480
  api.logger.error(`[PD] Error in subagent_ended: ${String(err)}`);
467
481
  }
468
- }
482
+ })
469
483
  );
470
484
 
471
485
  // ── Hook: Lifecycle ──
472
- api.on('before_reset', (event: PluginHookBeforeResetEvent, ctx: PluginHookAgentContext) => {
486
+ api.on('before_reset', guardHook('hook:before_reset', api.logger, (event: PluginHookBeforeResetEvent, ctx: PluginHookAgentContext) => {
473
487
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'before_reset');
474
488
  if (!workspaceDir) {
475
489
  api.logger.error(
@@ -480,9 +494,9 @@ const plugin = {
480
494
  return;
481
495
  }
482
496
  return handleBeforeReset(event, { ...ctx, workspaceDir });
483
- });
497
+ }));
484
498
 
485
- api.on('before_compaction', (event: PluginHookBeforeCompactionEvent, ctx: PluginHookAgentContext) => {
499
+ api.on('before_compaction', guardHook('hook:before_compaction', api.logger, (event: PluginHookBeforeCompactionEvent, ctx: PluginHookAgentContext) => {
486
500
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'before_compaction');
487
501
  if (!workspaceDir) {
488
502
  api.logger.error(
@@ -493,9 +507,9 @@ const plugin = {
493
507
  return;
494
508
  }
495
509
  return handleBeforeCompaction(event, { ...ctx, workspaceDir });
496
- });
510
+ }));
497
511
 
498
- api.on('after_compaction', (event: PluginHookAfterCompactionEvent, ctx: PluginHookAgentContext) => {
512
+ api.on('after_compaction', guardHook('hook:after_compaction', api.logger, (event: PluginHookAfterCompactionEvent, ctx: PluginHookAgentContext) => {
499
513
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'after_compaction');
500
514
  if (!workspaceDir) {
501
515
  api.logger.error(
@@ -506,17 +520,21 @@ const plugin = {
506
520
  return;
507
521
  }
508
522
  return handleAfterCompaction(event, { ...ctx, workspaceDir });
509
- });
523
+ }));
510
524
 
511
525
  // ── Service: Background Evolution Worker ──
512
526
  try {
513
527
  EvolutionWorkerService.api = api;
514
- api.registerService(EvolutionWorkerService);
515
- api.registerService(TrajectoryService);
516
- api.registerService(PDTaskService);
517
- api.registerService(CentralSyncService);
528
+ const guardedEvolutionWorker = guardService('service:evolution-worker', EvolutionWorkerService, api.logger);
529
+ if (guardedEvolutionWorker) api.registerService(guardedEvolutionWorker);
530
+ const guardedTrajectory = guardService('service:trajectory', TrajectoryService, api.logger);
531
+ if (guardedTrajectory) api.registerService(guardedTrajectory);
532
+ const guardedPdTask = guardService('service:pd-task', PDTaskService, api.logger);
533
+ if (guardedPdTask) api.registerService(guardedPdTask);
534
+ const guardedCentralSync = guardService('service:central-sync', CentralSyncService, api.logger);
535
+ if (guardedCentralSync) api.registerService(guardedCentralSync);
518
536
  } catch (err) {
519
- api.logger.error(`[PD] Failed to register EvolutionWorkerService: ${String(err)}`);
537
+ api.logger.error(`[PD] Failed to register services: ${String(err)}`);
520
538
  }
521
539
 
522
540
  // ── Slash Commands ──
package/src/types.ts CHANGED
@@ -37,13 +37,13 @@ export interface ContextInjectionConfig {
37
37
 
38
38
  /**
39
39
  * Default context injection configuration
40
- * Based on user requirements:
40
+ * Based on MVP-first strategy (ADR-0014):
41
41
  * - principles: always on (not configurable)
42
- * - thinkingOs: true (can be turned off)
42
+ * - thinkingOs: false by default (MVP-Quiet, user can opt-in via /pd-context)
43
43
  * - projectFocus: 'off' (default closed, user can enable)
44
44
  */
45
45
  export const defaultContextConfig: ContextInjectionConfig = {
46
- thinkingOs: true,
46
+ thinkingOs: false,
47
47
  projectFocus: 'off',
48
48
  evolutionContext: {
49
49
  enabled: true,
@@ -123,6 +123,7 @@ describe('PRI-212 plugin core anti-growth guard', () => {
123
123
  'evolution-engine.ts',
124
124
  'runtime-v2-prompt-activation-reader.ts',
125
125
  'workspace-guidance-migrator.ts',
126
+ 'surface-guard.ts',
126
127
  ] as const;
127
128
 
128
129
  // Category 6: Test files