create-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.
Files changed (217) hide show
  1. package/console/dist/server/config/feature-flags.d.ts +25 -0
  2. package/console/dist/server/config/feature-flags.js +101 -0
  3. package/console/dist/server/index.js +16 -0
  4. package/console/dist/server/models/FeedbackReportConsoleModel.d.ts +49 -0
  5. package/console/dist/server/models/FeedbackReportConsoleModel.js +242 -0
  6. package/console/dist/server/routes/feedback-reports.d.ts +18 -0
  7. package/console/dist/server/routes/feedback-reports.js +165 -0
  8. package/console/dist/ui/App.js +2 -1
  9. package/console/dist/ui/api.d.ts +26 -2
  10. package/console/dist/ui/api.js +18 -1
  11. package/console/dist/ui/components/app-sidebar.js +2 -1
  12. package/console/dist/ui/components/error-boundary.js +9 -2
  13. package/console/dist/ui/i18n/en.json +67 -1
  14. package/console/dist/ui/i18n/zh-CN.json +67 -1
  15. package/console/dist/ui/pages/ReportProblemPage.d.ts +1 -0
  16. package/console/dist/ui/pages/ReportProblemPage.js +186 -0
  17. package/console/dist/ui/pages/ReportProblemValidators.d.ts +51 -0
  18. package/console/dist/ui/pages/ReportProblemValidators.js +114 -0
  19. package/console/dist/web/assets/app.css +18 -0
  20. package/console/dist/web/assets/app.js +1047 -125
  21. package/console/package.json +1 -0
  22. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +24 -1
  23. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  24. package/core/dist/runtime-v2/__tests__/feedback/create-report.test.d.ts +2 -0
  25. package/core/dist/runtime-v2/__tests__/feedback/create-report.test.d.ts.map +1 -0
  26. package/core/dist/runtime-v2/__tests__/feedback/create-report.test.js +402 -0
  27. package/core/dist/runtime-v2/__tests__/feedback/create-report.test.js.map +1 -0
  28. package/core/dist/runtime-v2/__tests__/feedback/privacy-preview.test.d.ts +2 -0
  29. package/core/dist/runtime-v2/__tests__/feedback/privacy-preview.test.d.ts.map +1 -0
  30. package/core/dist/runtime-v2/__tests__/feedback/privacy-preview.test.js +128 -0
  31. package/core/dist/runtime-v2/__tests__/feedback/privacy-preview.test.js.map +1 -0
  32. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.d.ts +2 -0
  33. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.d.ts.map +1 -0
  34. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.js +231 -0
  35. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.js.map +1 -0
  36. package/core/dist/runtime-v2/__tests__/feedback/render-github-url.test.d.ts +2 -0
  37. package/core/dist/runtime-v2/__tests__/feedback/render-github-url.test.d.ts.map +1 -0
  38. package/core/dist/runtime-v2/__tests__/feedback/render-github-url.test.js +102 -0
  39. package/core/dist/runtime-v2/__tests__/feedback/render-github-url.test.js.map +1 -0
  40. package/core/dist/runtime-v2/__tests__/feedback/render-markdown.test.d.ts +2 -0
  41. package/core/dist/runtime-v2/__tests__/feedback/render-markdown.test.d.ts.map +1 -0
  42. package/core/dist/runtime-v2/__tests__/feedback/render-markdown.test.js +130 -0
  43. package/core/dist/runtime-v2/__tests__/feedback/render-markdown.test.js.map +1 -0
  44. package/core/dist/runtime-v2/__tests__/workspace-guidance-migration.test.d.ts +2 -0
  45. package/core/dist/runtime-v2/__tests__/workspace-guidance-migration.test.d.ts.map +1 -0
  46. package/core/dist/runtime-v2/__tests__/workspace-guidance-migration.test.js +328 -0
  47. package/core/dist/runtime-v2/__tests__/workspace-guidance-migration.test.js.map +1 -0
  48. package/core/dist/runtime-v2/activation/__tests__/prompt-activation-reader-contract.test.d.ts +2 -0
  49. package/core/dist/runtime-v2/activation/__tests__/prompt-activation-reader-contract.test.d.ts.map +1 -0
  50. package/core/dist/runtime-v2/activation/__tests__/prompt-activation-reader-contract.test.js +343 -0
  51. package/core/dist/runtime-v2/activation/__tests__/prompt-activation-reader-contract.test.js.map +1 -0
  52. package/core/dist/runtime-v2/activation/index.d.ts +2 -0
  53. package/core/dist/runtime-v2/activation/index.d.ts.map +1 -1
  54. package/core/dist/runtime-v2/activation/index.js +1 -0
  55. package/core/dist/runtime-v2/activation/index.js.map +1 -1
  56. package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.d.ts +34 -0
  57. package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.d.ts.map +1 -0
  58. package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.js +109 -0
  59. package/core/dist/runtime-v2/activation/prompt-activation-reader-contract.js.map +1 -0
  60. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +13 -0
  61. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
  62. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.d.ts +2 -0
  63. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.d.ts.map +1 -0
  64. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.js +141 -0
  65. package/core/dist/runtime-v2/feature-flags/__tests__/plugin-surface-registry.test.js.map +1 -0
  66. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  67. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +4 -1
  68. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  69. package/core/dist/runtime-v2/feature-flags/index.d.ts +2 -0
  70. package/core/dist/runtime-v2/feature-flags/index.d.ts.map +1 -1
  71. package/core/dist/runtime-v2/feature-flags/index.js +1 -0
  72. package/core/dist/runtime-v2/feature-flags/index.js.map +1 -1
  73. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.d.ts +25 -0
  74. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.d.ts.map +1 -0
  75. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js +233 -0
  76. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js.map +1 -0
  77. package/core/dist/runtime-v2/feedback/create-report.d.ts +14 -0
  78. package/core/dist/runtime-v2/feedback/create-report.d.ts.map +1 -0
  79. package/core/dist/runtime-v2/feedback/create-report.js +257 -0
  80. package/core/dist/runtime-v2/feedback/create-report.js.map +1 -0
  81. package/core/dist/runtime-v2/feedback/feedback-types.d.ts +120 -0
  82. package/core/dist/runtime-v2/feedback/feedback-types.d.ts.map +1 -0
  83. package/core/dist/runtime-v2/feedback/feedback-types.js +243 -0
  84. package/core/dist/runtime-v2/feedback/feedback-types.js.map +1 -0
  85. package/core/dist/runtime-v2/feedback/index.d.ts +9 -0
  86. package/core/dist/runtime-v2/feedback/index.d.ts.map +1 -0
  87. package/core/dist/runtime-v2/feedback/index.js +11 -0
  88. package/core/dist/runtime-v2/feedback/index.js.map +1 -0
  89. package/core/dist/runtime-v2/feedback/internal-guards.d.ts +2 -0
  90. package/core/dist/runtime-v2/feedback/internal-guards.d.ts.map +1 -0
  91. package/core/dist/runtime-v2/feedback/internal-guards.js +6 -0
  92. package/core/dist/runtime-v2/feedback/internal-guards.js.map +1 -0
  93. package/core/dist/runtime-v2/feedback/privacy-preview.d.ts +14 -0
  94. package/core/dist/runtime-v2/feedback/privacy-preview.d.ts.map +1 -0
  95. package/core/dist/runtime-v2/feedback/privacy-preview.js +128 -0
  96. package/core/dist/runtime-v2/feedback/privacy-preview.js.map +1 -0
  97. package/core/dist/runtime-v2/feedback/redact-sensitive.d.ts +37 -0
  98. package/core/dist/runtime-v2/feedback/redact-sensitive.d.ts.map +1 -0
  99. package/core/dist/runtime-v2/feedback/redact-sensitive.js +272 -0
  100. package/core/dist/runtime-v2/feedback/redact-sensitive.js.map +1 -0
  101. package/core/dist/runtime-v2/feedback/render-github-url.d.ts +21 -0
  102. package/core/dist/runtime-v2/feedback/render-github-url.d.ts.map +1 -0
  103. package/core/dist/runtime-v2/feedback/render-github-url.js +57 -0
  104. package/core/dist/runtime-v2/feedback/render-github-url.js.map +1 -0
  105. package/core/dist/runtime-v2/feedback/render-markdown.d.ts +12 -0
  106. package/core/dist/runtime-v2/feedback/render-markdown.d.ts.map +1 -0
  107. package/core/dist/runtime-v2/feedback/render-markdown.js +166 -0
  108. package/core/dist/runtime-v2/feedback/render-markdown.js.map +1 -0
  109. package/core/dist/runtime-v2/feedback/safe-stringify.d.ts +6 -0
  110. package/core/dist/runtime-v2/feedback/safe-stringify.d.ts.map +1 -0
  111. package/core/dist/runtime-v2/feedback/safe-stringify.js +115 -0
  112. package/core/dist/runtime-v2/feedback/safe-stringify.js.map +1 -0
  113. package/core/dist/runtime-v2/index.d.ts +8 -1
  114. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  115. package/core/dist/runtime-v2/index.js +12 -1
  116. package/core/dist/runtime-v2/index.js.map +1 -1
  117. package/core/dist/runtime-v2/workspace-guidance-migration.d.ts +12 -0
  118. package/core/dist/runtime-v2/workspace-guidance-migration.d.ts.map +1 -0
  119. package/core/dist/runtime-v2/workspace-guidance-migration.js +193 -0
  120. package/core/dist/runtime-v2/workspace-guidance-migration.js.map +1 -0
  121. package/core/package.json +4 -0
  122. package/package.json +1 -1
  123. package/plugin/dist/commands/context.js +5 -5
  124. package/plugin/dist/commands/strategy.d.ts +0 -1
  125. package/plugin/dist/commands/strategy.js +0 -14
  126. package/plugin/dist/core/runtime-v2-prompt-activation-reader.d.ts +4 -13
  127. package/plugin/dist/core/runtime-v2-prompt-activation-reader.js +36 -74
  128. package/plugin/dist/core/surface-guard.d.ts +27 -0
  129. package/plugin/dist/core/surface-guard.js +82 -0
  130. package/plugin/dist/core/workspace-guidance-migrator.d.ts +12 -0
  131. package/plugin/dist/core/workspace-guidance-migrator.js +141 -0
  132. package/plugin/dist/hooks/prompt.js +20 -143
  133. package/plugin/dist/i18n/commands.js +2 -14
  134. package/plugin/dist/index.d.ts +27 -0
  135. package/plugin/dist/index.js +180 -65
  136. package/plugin/dist/service/correction-observer-service.d.ts +11 -0
  137. package/plugin/dist/service/correction-observer-service.js +172 -0
  138. package/plugin/dist/service/evolution-worker.js +1 -110
  139. package/plugin/dist/types.d.ts +2 -2
  140. package/plugin/dist/types.js +3 -3
  141. package/plugin/templates/langs/en/core/BOOTSTRAP.md +5 -19
  142. package/plugin/templates/langs/en/principles/THINKING_OS.md +1 -1
  143. package/plugin/templates/langs/en/skills/admin/SKILL.md +2 -2
  144. package/plugin/templates/langs/en/skills/bootstrap-tools/SKILL.md +1 -1
  145. package/plugin/templates/langs/en/skills/init-strategy/SKILL.md +1 -1
  146. package/plugin/templates/langs/en/skills/pd-mentor/SKILL.md +8 -23
  147. package/plugin/templates/langs/en/skills/reflection/SKILL.md +2 -2
  148. package/plugin/templates/langs/en/skills/report/SKILL.md +1 -1
  149. package/plugin/templates/langs/zh/core/BOOTSTRAP.md +3 -16
  150. package/plugin/templates/langs/zh/principles/THINKING_OS.md +1 -1
  151. package/plugin/templates/langs/zh/skills/admin/SKILL.md +2 -2
  152. package/plugin/templates/langs/zh/skills/bootstrap-tools/SKILL.md +1 -1
  153. package/plugin/templates/langs/zh/skills/init-strategy/SKILL.md +1 -1
  154. package/plugin/templates/langs/zh/skills/pd-mentor/SKILL.md +7 -22
  155. package/plugin/templates/langs/zh/skills/reflection/SKILL.md +2 -2
  156. package/plugin/templates/langs/zh/skills/report/SKILL.md +1 -1
  157. package/plugin/templates/workspace/.principles/THINKING_OS.md +1 -1
  158. package/templates/langs/en/core/BOOTSTRAP.md +1 -1
  159. package/templates/langs/en/principles/THINKING_OS.md +1 -1
  160. package/templates/langs/en/skills/admin/SKILL.md +2 -2
  161. package/templates/langs/en/skills/pd-grooming/SKILL.md +1 -1
  162. package/templates/langs/en/skills/reflection/SKILL.md +2 -2
  163. package/templates/langs/en/skills/report/SKILL.md +1 -1
  164. package/templates/langs/zh/core/BOOTSTRAP.md +1 -1
  165. package/templates/langs/zh/principles/THINKING_OS.md +1 -1
  166. package/templates/langs/zh/skills/admin/SKILL.md +2 -2
  167. package/templates/langs/zh/skills/pd-grooming/SKILL.md +1 -1
  168. package/templates/langs/zh/skills/reflection/SKILL.md +2 -2
  169. package/templates/langs/zh/skills/report/SKILL.md +1 -1
  170. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/EXAMPLES.md +0 -63
  171. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/REFERENCE.md +0 -136
  172. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/SKILL.md +0 -67
  173. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +0 -143
  174. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +0 -107
  175. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +0 -107
  176. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +0 -95
  177. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +0 -98
  178. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +0 -58
  179. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +0 -190
  180. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +0 -310
  181. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +0 -683
  182. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +0 -604
  183. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +0 -32
  184. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +0 -707
  185. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +0 -3455
  186. package/plugin/templates/langs/en/skills/evolve-system/SKILL.md +0 -46
  187. package/plugin/templates/langs/en/skills/manage-okr/SKILL.md +0 -96
  188. package/plugin/templates/langs/en/skills/pd-daily/SKILL.md +0 -199
  189. package/plugin/templates/langs/en/skills/pd-grooming/SKILL.md +0 -46
  190. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/EXAMPLES.md +0 -63
  191. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/REFERENCE.md +0 -136
  192. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/SKILL.md +0 -67
  193. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +0 -143
  194. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +0 -107
  195. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +0 -107
  196. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +0 -111
  197. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +0 -95
  198. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +0 -98
  199. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +0 -58
  200. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +0 -190
  201. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +0 -2
  202. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +0 -310
  203. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +0 -683
  204. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +0 -604
  205. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +0 -32
  206. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +0 -707
  207. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +0 -3455
  208. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/test/archive.test.mjs +0 -230
  209. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/test/contract-enforcement.test.mjs +0 -672
  210. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/test/decision.test.mjs +0 -1321
  211. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +0 -1435
  212. package/plugin/templates/langs/zh/skills/evolve-system/SKILL.md +0 -46
  213. package/plugin/templates/langs/zh/skills/manage-okr/SKILL.md +0 -109
  214. package/plugin/templates/langs/zh/skills/pd-daily/SKILL.md +0 -283
  215. package/plugin/templates/langs/zh/skills/pd-grooming/SKILL.md +0 -46
  216. package/plugin/templates/workspace/PLAN.md +0 -2
  217. package/templates/workspace/PLAN.md +0 -2
@@ -1,4 +1,7 @@
1
1
  import * as path from 'path';
2
+ import * as fs from 'fs';
3
+ import * as yaml from 'js-yaml';
4
+ import { computeEffectiveFlags, DEFAULT_FEATURE_FLAGS } from '@principles/core/runtime-v2';
2
5
  import { classifyTask } from './core/local-worker-routing.js';
3
6
  import { completeShadowObservation, recordShadowRouting } from './core/shadow-observation-registry.js';
4
7
  import { getCommandDescription } from './i18n/commands.js';
@@ -10,7 +13,7 @@ import { handleBeforeReset, handleBeforeCompaction, handleAfterCompaction } from
10
13
  import { handleLlmOutput } from './hooks/llm.js';
11
14
  import { handleSubagentEnded } from './hooks/subagent.js';
12
15
  import * as TrajectoryCollector from './hooks/trajectory-collector.js';
13
- import { handleInitStrategy, handleManageOkr } from './commands/strategy.js';
16
+ import { handleInitStrategy } from './commands/strategy.js';
14
17
  import { handleBootstrapTools, handleResearchTools } from './commands/capabilities.js';
15
18
  import { handleThinkingOs } from './commands/thinking-os.js';
16
19
  import { handlePainCommand, handlePainReportCommand } from './commands/pain.js';
@@ -27,26 +30,110 @@ import { handleExportCommand } from './commands/export.js';
27
30
  import { handleSamplesCommand } from './commands/samples.js';
28
31
  import { handleWorkflowDebugCommand } from './commands/workflow-debug.js';
29
32
  import { EvolutionWorkerService } from './service/evolution-worker.js';
33
+ import { CorrectionObserverService } from './service/correction-observer-service.js';
30
34
  import { TrajectoryService } from './service/trajectory-service.js';
31
35
  import { PDTaskService } from './core/pd-task-service.js';
32
36
  import { CentralSyncService } from './service/central-sync-service.js';
33
37
  import { ensureWorkspaceTemplates } from './core/init.js';
34
38
  import { migrateDirectoryStructure } from './core/migration.js';
39
+ import { migrateStaleWorkspaceGuidance } from './core/workspace-guidance-migrator.js';
35
40
  import { SystemLogger } from './core/system-logger.js';
36
41
  import { PathResolver } from './core/path-resolver.js';
37
42
  import { resolveCommandWorkspaceDir, resolveToolHookWorkspaceDirSafe } from './utils/workspace-resolver.js';
38
43
  import { computeRuntimeShadowTaskFingerprint, PD_LOCAL_PROFILES } from './utils/shadow-fingerprint.js';
39
44
  import { validateWorkspaceDir } from './core/workspace-dir-validation.js';
40
45
  import { resolveWorkspaceDirFromApi } from './core/path-resolver.js';
41
- // Track initialization to avoid repeated calls
42
- let workspaceInitialized = false;
43
- // Track started evolution workers — one per workspace
46
+ import { checkSurfaceGuard, guardHook, guardService } from './core/surface-guard.js';
47
+ // Track started workspaces — one-time init + evolution worker per workspace
44
48
  const startedWorkspaces = new Set();
45
49
  const HOOK_WORKSPACE_RESOLUTION_NEXT_ACTION = 'verify gateway plugin activation and hook workspace binding; ' +
46
50
  'migrate live hook workspace resolution to PD-owned canonical configuration before relying on config-based recovery';
47
51
  // Map from childSessionKey → shadowObservationId
48
52
  // Used to complete shadow observations when subagent ends
49
53
  const pendingShadowObservations = new Map();
54
+ // ── Feature Flag Loader (plugin I/O boundary) ─────────────────────────────
55
+ // Reads workspace feature-flags.yaml and checks a specific flag.
56
+ // Returns the flag definition with effective enabled state.
57
+ const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
58
+ function isRecord(value) {
59
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
60
+ }
61
+ function loadFeatureFlagFromWorkspace(workspaceDir, flagId, logger) {
62
+ const configPath = path.join(workspaceDir, '.pd', 'feature-flags.yaml');
63
+ if (!fs.existsSync(configPath)) {
64
+ const flags = computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath);
65
+ const flag = flags.flags[flagId];
66
+ return { enabled: flag?.enabled ?? false, source: 'defaults' };
67
+ }
68
+ let raw;
69
+ try {
70
+ raw = fs.readFileSync(configPath, 'utf8');
71
+ }
72
+ catch (e) {
73
+ const msg = e instanceof Error ? e.message : String(e);
74
+ logger?.warn?.(`[PD:FeatureFlags] Feature flags unreadable: ${msg} — using defaults`);
75
+ const flags = computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath);
76
+ const flag = flags.flags[flagId];
77
+ return { enabled: flag?.enabled ?? false, source: 'defaults' };
78
+ }
79
+ let parsed;
80
+ try {
81
+ parsed = yaml.load(raw, { schema: yaml.JSON_SCHEMA });
82
+ }
83
+ catch (e) {
84
+ const parseMsg = e instanceof Error ? e.message : String(e);
85
+ logger?.warn?.(`[PD:FeatureFlags] Feature flags YAML parse error: ${parseMsg} — using defaults`);
86
+ const flags = computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath);
87
+ const flag = flags.flags[flagId];
88
+ return { enabled: flag?.enabled ?? false, source: 'defaults' };
89
+ }
90
+ if (!isRecord(parsed)) {
91
+ logger?.warn?.(`[PD:FeatureFlags] Feature flags not a mapping — using defaults`);
92
+ const flags = computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath);
93
+ const flag = flags.flags[flagId];
94
+ return { enabled: flag?.enabled ?? false, source: 'defaults' };
95
+ }
96
+ // parsed is now narrowed to Record<string, unknown> by isRecord guard
97
+ const parsedRecord = Object.create(null);
98
+ for (const key of Object.keys(parsed)) {
99
+ if (DANGEROUS_KEYS.has(key))
100
+ continue;
101
+ if (Object.hasOwn(parsed, key)) {
102
+ parsedRecord[key] = parsed[key];
103
+ }
104
+ }
105
+ const flags = computeEffectiveFlags(parsedRecord, DEFAULT_FEATURE_FLAGS, configPath);
106
+ const flag = flags.flags[flagId];
107
+ return { enabled: flag?.enabled ?? false, source: flags.source };
108
+ }
109
+ export function shouldStartEvolutionWorker(workspaceDir, logger) {
110
+ const flag = loadFeatureFlagFromWorkspace(workspaceDir, 'evolution_worker', logger);
111
+ if (flag.enabled) {
112
+ return { shouldStart: true, flagSource: flag.source, disabledInfo: null };
113
+ }
114
+ const disabledInfo = JSON.stringify({
115
+ reason: 'mvp_quiet_per_adr0014',
116
+ nextAction: 'set evolution_worker.enabled=true in .pd/feature-flags.yaml to enable',
117
+ featureFlag: 'evolution_worker',
118
+ boundedContext: 'legacy_evolution_worker',
119
+ flagSource: flag.source,
120
+ });
121
+ return { shouldStart: false, flagSource: flag.source, disabledInfo };
122
+ }
123
+ export function shouldStartCorrectionObserver(workspaceDir, logger) {
124
+ const flag = loadFeatureFlagFromWorkspace(workspaceDir, 'correction_observer', logger);
125
+ if (flag.enabled) {
126
+ return { shouldStart: true, flagSource: flag.source, disabledInfo: null };
127
+ }
128
+ const disabledInfo = JSON.stringify({
129
+ reason: 'correction_observer_disabled',
130
+ nextAction: 'set correction_observer.enabled=true in .pd/feature-flags.yaml to enable',
131
+ featureFlag: 'correction_observer',
132
+ boundedContext: 'correction_observer_service',
133
+ flagSource: flag.source,
134
+ });
135
+ return { shouldStart: false, flagSource: flag.source, disabledInfo };
136
+ }
50
137
  const plugin = {
51
138
  name: "Principles Disciple",
52
139
  description: "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
@@ -68,9 +155,21 @@ const plugin = {
68
155
  }
69
156
  }, 1000);
70
157
  healthCheckTimer.unref(); // Don't keep process alive for health check
158
+ // ── MVP Surface Guard (PRI-289): Verify surface classification ──
159
+ const surfaceGuard = checkSurfaceGuard();
160
+ if (!surfaceGuard.passed) {
161
+ for (const violation of surfaceGuard.violations) {
162
+ api.logger.error(`[PD:surface-guard] VIOLATION: ${violation}`);
163
+ }
164
+ }
165
+ api.logger.info(`[PD:surface-guard] Core surfaces: ${surfaceGuard.enabledCoreSurfaces.join(', ')}`);
166
+ api.logger.info(`[PD:surface-guard] Disabled non-core surfaces: ${surfaceGuard.disabledNonCoreSurfaces.length}`);
167
+ for (const warning of surfaceGuard.warnings) {
168
+ api.logger.warn(`[PD:surface-guard] ${warning}`);
169
+ }
71
170
  const language = api.pluginConfig?.language || 'en';
72
171
  // ── Hook: Prompt Building ──
73
- api.on('before_prompt_build', async (event, ctx) => {
172
+ api.on('before_prompt_build', guardHook('hook:before_prompt_build', api.logger, async (event, ctx) => {
74
173
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'before_prompt_build');
75
174
  if (!workspaceDir) {
76
175
  api.logger.error(`[PD:before_prompt_build] workspaceDir resolution failed. ` +
@@ -81,26 +180,46 @@ const plugin = {
81
180
  return;
82
181
  }
83
182
  try {
84
- if (!workspaceInitialized) {
183
+ if (!startedWorkspaces.has(workspaceDir)) {
184
+ startedWorkspaces.add(workspaceDir);
85
185
  migrateDirectoryStructure(api, workspaceDir);
186
+ migrateStaleWorkspaceGuidance(api, workspaceDir);
86
187
  ensureWorkspaceTemplates(api, workspaceDir, language);
87
188
  SystemLogger.log(workspaceDir, 'SYSTEM_BOOT', `Principles Disciple online. Language: ${language}`);
88
- workspaceInitialized = true;
89
- }
90
- // ── Start EvolutionWorker for THIS workspace ──
91
- // Each agent has its own heartbeat task. When before_prompt_build fires,
92
- // it fires for the current agent's workspaceDir. Start one EvolutionWorker
93
- // per workspace so each agent's pain signals are processed independently.
94
- if (!startedWorkspaces.has(workspaceDir)) {
95
- startedWorkspaces.add(workspaceDir);
96
- EvolutionWorkerService.api = api;
97
- EvolutionWorkerService.start({
98
- config: api.config,
99
- workspaceDir,
100
- stateDir: path.join(workspaceDir, '.state'),
101
- logger: api.logger,
102
- });
103
- api.logger.info(`[PD] EvolutionWorker started for workspace: ${workspaceDir}`);
189
+ // ── Start EvolutionWorker for THIS workspace ──
190
+ // Gated behind evolution_worker feature flag (MVP-Quiet, default OFF per ADR-0014).
191
+ const gate = shouldStartEvolutionWorker(workspaceDir, api.logger);
192
+ if (gate.shouldStart) {
193
+ EvolutionWorkerService.api = api;
194
+ EvolutionWorkerService.start({
195
+ config: api.config,
196
+ workspaceDir,
197
+ stateDir: path.join(workspaceDir, '.state'),
198
+ logger: api.logger,
199
+ });
200
+ api.logger.info(`[PD] EvolutionWorker started for workspace: ${workspaceDir} (flag source: ${gate.flagSource})`);
201
+ }
202
+ else {
203
+ // Structured observability per ERR-002: no silent skip
204
+ api.logger.info(`[PD] EvolutionWorker NOT started for workspace: ${workspaceDir}. ${gate.disabledInfo}`);
205
+ SystemLogger.log(workspaceDir, 'EVOLUTION_WORKER_DISABLED', gate.disabledInfo ?? '');
206
+ }
207
+ // ── Start CorrectionObserver for THIS workspace ──
208
+ // MVP-Core per ADR-0014 amendment, independently owned (PRI-293).
209
+ const corrGate = shouldStartCorrectionObserver(workspaceDir, api.logger);
210
+ if (corrGate.shouldStart) {
211
+ CorrectionObserverService.start({
212
+ config: api.config,
213
+ workspaceDir,
214
+ stateDir: path.join(workspaceDir, '.state'),
215
+ logger: api.logger,
216
+ });
217
+ api.logger.info(`[PD] CorrectionObserver started for workspace: ${workspaceDir} (flag source: ${corrGate.flagSource})`);
218
+ }
219
+ else {
220
+ api.logger.info(`[PD] CorrectionObserver NOT started for workspace: ${workspaceDir}. ${corrGate.disabledInfo}`);
221
+ SystemLogger.log(workspaceDir, 'CORRECTION_OBSERVER_DISABLED', corrGate.disabledInfo ?? '');
222
+ }
104
223
  }
105
224
  const result = await handleBeforePromptBuild(event, { ...ctx, api: api, workspaceDir });
106
225
  // Record success
@@ -118,9 +237,9 @@ const plugin = {
118
237
  });
119
238
  api.logger.error(`[PD] Error in before_prompt_build: ${String(err)}`);
120
239
  }
121
- });
240
+ }));
122
241
  // ── Hook: Security Gate ──
123
- api.on('before_tool_call', (event, ctx) => {
242
+ api.on('before_tool_call', guardHook('hook:before_tool_call', api.logger, (event, ctx) => {
124
243
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'before_tool_call');
125
244
  if (!workspaceDir) {
126
245
  api.logger.error(`[PD:before_tool_call] workspaceDir resolution failed. ` +
@@ -146,9 +265,9 @@ const plugin = {
146
265
  }, { flushImmediately: true });
147
266
  api.logger.error(`[PD] Error in before_tool_call: ${String(err)}`);
148
267
  }
149
- });
268
+ }));
150
269
  // ── Hook: Pain & Trust ──
151
- api.on('after_tool_call', (event, ctx) => {
270
+ api.on('after_tool_call', guardHook('hook:after_tool_call', api.logger, (event, ctx) => {
152
271
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'after_tool_call');
153
272
  if (!workspaceDir) {
154
273
  api.logger.error(`[PD:after_tool_call] workspaceDir resolution failed. ` +
@@ -173,9 +292,9 @@ const plugin = {
173
292
  }, { flushImmediately: true });
174
293
  api.logger.error(`[PD:EmpathyObserver] Error in after_tool_call: ${String(err)}`);
175
294
  }
176
- });
295
+ }));
177
296
  // ── Hook: LLM Analysis ──
178
- api.on('llm_output', (event, ctx) => {
297
+ api.on('llm_output', guardHook('hook:llm_output', api.logger, (event, ctx) => {
179
298
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'llm_output');
180
299
  if (!workspaceDir) {
181
300
  api.logger.error(`[PD:llm_output] workspaceDir resolution failed. ` +
@@ -200,10 +319,10 @@ const plugin = {
200
319
  });
201
320
  api.logger.error(`[PD] Error in llm_output: ${String(err)}`);
202
321
  }
203
- });
322
+ }));
204
323
  // ── Hook: Trajectory Collection (Behavior Evolution Phase 0) ──
205
324
  // Note: after_tool_call and llm_output are safe to collect
206
- api.on('after_tool_call', (event, ctx) => {
325
+ api.on('after_tool_call', guardHook('hook:after_tool_call.trajectory', api.logger, (event, ctx) => {
207
326
  try {
208
327
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'trajectory.after_tool_call');
209
328
  if (!workspaceDir)
@@ -214,8 +333,8 @@ const plugin = {
214
333
  catch (_err) {
215
334
  // Non-critical: don't log, just skip
216
335
  }
217
- });
218
- api.on('llm_output', (event, ctx) => {
336
+ }));
337
+ api.on('llm_output', guardHook('hook:llm_output.trajectory', api.logger, (event, ctx) => {
219
338
  try {
220
339
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'trajectory.llm_output');
221
340
  if (!workspaceDir)
@@ -226,9 +345,9 @@ const plugin = {
226
345
  catch (_err) {
227
346
  // Non-critical: don't log, just skip
228
347
  }
229
- });
348
+ }));
230
349
  // ── Hook: Subagent Loop Closure ──
231
- api.on('subagent_spawning', (event, _ctx) => {
350
+ api.on('subagent_spawning', guardHook('hook:subagent_spawning', api.logger, (event, _ctx) => {
232
351
  try {
233
352
  // FIX (B): Never fall back to '.' — fail-fast with ERROR log if workspaceDir cannot be resolved.
234
353
  // For subagent hooks, we use event.agentId as the target agent for workspace resolution.
@@ -266,8 +385,8 @@ const plugin = {
266
385
  api.logger.error(`[PD] Error in subagent_spawning shadow routing: ${String(err)}`);
267
386
  return { status: 'ok' }; // Don't block spawn on shadow observation errors
268
387
  }
269
- });
270
- api.on('subagent_ended', (event, ctx) => {
388
+ }));
389
+ api.on('subagent_ended', guardHook('hook:subagent_ended', api.logger, (event, ctx) => {
271
390
  try {
272
391
  // FIX (B): Never fall back to '.' — fail-fast with ERROR log if workspaceDir cannot be resolved.
273
392
  const workspaceDir = resolveWorkspaceDirFromApi(api, undefined);
@@ -301,9 +420,9 @@ const plugin = {
301
420
  catch (err) {
302
421
  api.logger.error(`[PD] Error in subagent_ended: ${String(err)}`);
303
422
  }
304
- });
423
+ }));
305
424
  // ── Hook: Lifecycle ──
306
- api.on('before_reset', (event, ctx) => {
425
+ api.on('before_reset', guardHook('hook:before_reset', api.logger, (event, ctx) => {
307
426
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'before_reset');
308
427
  if (!workspaceDir) {
309
428
  api.logger.error(`[PD:before_reset] workspaceDir resolution failed. ` +
@@ -312,8 +431,8 @@ const plugin = {
312
431
  return;
313
432
  }
314
433
  return handleBeforeReset(event, { ...ctx, workspaceDir });
315
- });
316
- api.on('before_compaction', (event, ctx) => {
434
+ }));
435
+ api.on('before_compaction', guardHook('hook:before_compaction', api.logger, (event, ctx) => {
317
436
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'before_compaction');
318
437
  if (!workspaceDir) {
319
438
  api.logger.error(`[PD:before_compaction] workspaceDir resolution failed. ` +
@@ -322,8 +441,8 @@ const plugin = {
322
441
  return;
323
442
  }
324
443
  return handleBeforeCompaction(event, { ...ctx, workspaceDir });
325
- });
326
- api.on('after_compaction', (event, ctx) => {
444
+ }));
445
+ api.on('after_compaction', guardHook('hook:after_compaction', api.logger, (event, ctx) => {
327
446
  const workspaceDir = resolveToolHookWorkspaceDirSafe(ctx, api, 'after_compaction');
328
447
  if (!workspaceDir) {
329
448
  api.logger.error(`[PD:after_compaction] workspaceDir resolution failed. ` +
@@ -332,17 +451,28 @@ const plugin = {
332
451
  return;
333
452
  }
334
453
  return handleAfterCompaction(event, { ...ctx, workspaceDir });
335
- });
454
+ }));
336
455
  // ── Service: Background Evolution Worker ──
337
456
  try {
338
457
  EvolutionWorkerService.api = api;
339
- api.registerService(EvolutionWorkerService);
340
- api.registerService(TrajectoryService);
341
- api.registerService(PDTaskService);
342
- api.registerService(CentralSyncService);
458
+ const guardedEvolutionWorker = guardService('service:evolution-worker', EvolutionWorkerService, api.logger);
459
+ if (guardedEvolutionWorker)
460
+ api.registerService(guardedEvolutionWorker);
461
+ const guardedCorrectionObserver = guardService('service:correction-observer', CorrectionObserverService, api.logger);
462
+ if (guardedCorrectionObserver)
463
+ api.registerService(guardedCorrectionObserver);
464
+ const guardedTrajectory = guardService('service:trajectory', TrajectoryService, api.logger);
465
+ if (guardedTrajectory)
466
+ api.registerService(guardedTrajectory);
467
+ const guardedPdTask = guardService('service:pd-task', PDTaskService, api.logger);
468
+ if (guardedPdTask)
469
+ api.registerService(guardedPdTask);
470
+ const guardedCentralSync = guardService('service:central-sync', CentralSyncService, api.logger);
471
+ if (guardedCentralSync)
472
+ api.registerService(guardedCentralSync);
343
473
  }
344
474
  catch (err) {
345
- api.logger.error(`[PD] Failed to register EvolutionWorkerService: ${String(err)}`);
475
+ api.logger.error(`[PD] Failed to register services: ${String(err)}`);
346
476
  }
347
477
  // ── Slash Commands ──
348
478
  // Register command with optional short alias
@@ -363,20 +493,9 @@ const plugin = {
363
493
  }
364
494
  };
365
495
  registerCommandWithAlias('pd-init', 'pdi', getCommandDescription('pd-init', language), (ctx) => handleInitStrategy(ctx));
366
- registerCommandWithAlias('pd-okr', 'pdk', getCommandDescription('pd-okr', language), (ctx) => handleManageOkr(ctx));
367
496
  registerCommandWithAlias('pd-bootstrap', 'pdb', getCommandDescription('pd-bootstrap', language), (ctx) => handleBootstrapTools(ctx));
368
497
  registerCommandWithAlias('pd-research', 'pdr', getCommandDescription('pd-research', language), (ctx) => handleResearchTools(ctx));
369
498
  registerCommandWithAlias('pd-thinking', 'pdt', getCommandDescription('pd-thinking', language), (ctx) => handleThinkingOs(ctx), { acceptsArgs: true });
370
- registerCommandWithAlias('pd-daily', 'pdd', getCommandDescription('pd-daily', language), () => ({
371
- text: language === 'zh'
372
- ? "请执行 pd-daily 技能来配置并发送进化日报。系统将引导你完成配置流程,包括发送时间、渠道和报告风格偏好。"
373
- : "Please execute the pd-daily skill to configure and send your daily evolution report. The system will guide you through the configuration process."
374
- }));
375
- registerCommandWithAlias('pd-grooming', 'pdg', getCommandDescription('pd-grooming', language), () => ({
376
- text: language === 'zh'
377
- ? "请执行 pd-grooming 技能来执行大扫除。例如输入: '执行 pd-grooming 技能'"
378
- : "Please execute the pd-grooming skill to clean up. For example: 'Execute pd-grooming skill'"
379
- }));
380
499
  registerCommandWithAlias('pd-help', 'pdh', getCommandDescription('pd-help', language), () => {
381
500
  if (language === 'zh') {
382
501
  return { text: `
@@ -392,10 +511,7 @@ const plugin = {
392
511
  ## 状态查询
393
512
  | 短命令 | 长命令 | 用途 |
394
513
  |--------|--------|------|
395
- | \`/pdk\` | \`/pd-okr\` | OKR 目标管理 |
396
514
  | \`/pdt\` | \`/pd-thinking\` | 思维模型管理 |
397
- | \`/pdd\` | \`/pd-daily\` | 进化日报 |
398
- | \`/pdg\` | \`/pd-grooming\` | 工作区清理 |
399
515
 
400
516
  ## 其他命令
401
517
  | 命令 | 用途 |
@@ -424,10 +540,7 @@ const plugin = {
424
540
  ## Status
425
541
  | Short | Full | Purpose |
426
542
  |-------|------|---------|
427
- | \`/pdk\` | \`/pd-okr\` | OKR goal management |
428
543
  | \`/pdt\` | \`/pd-thinking\` | Mental model management |
429
- | \`/pdd\` | \`/pd-daily\` | Evolution report |
430
- | \`/pdg\` | \`/pd-grooming\` | Workspace cleanup |
431
544
 
432
545
  ## Other Commands
433
546
  | Command | Purpose |
@@ -689,4 +802,6 @@ const plugin = {
689
802
  }
690
803
  };
691
804
  export { PrincipleTreeLedgerAdapter } from './core/principle-tree-ledger-adapter.js';
805
+ /* istanbul ignore next — test exports for evolution worker gate */
806
+ export { loadFeatureFlagFromWorkspace, isRecord };
692
807
  export default plugin;
@@ -0,0 +1,11 @@
1
+ import type { OpenClawPluginServiceContext, PluginLogger } from '../openclaw-sdk.js';
2
+ import { WorkspaceContext } from '../core/workspace-context.js';
3
+ import { CorrectionObserver } from '@principles/core/runtime-v2';
4
+ export interface CorrectionObserverServiceShape {
5
+ id: string;
6
+ start: (ctx: OpenClawPluginServiceContext) => void;
7
+ stop?: (ctx: OpenClawPluginServiceContext) => void;
8
+ }
9
+ export declare function resolveCorrectionObserver(wctx: WorkspaceContext, logger?: Pick<PluginLogger, 'info' | 'warn' | 'error' | 'debug'>): CorrectionObserver | null;
10
+ export declare function runCorrectionObserverCycle(wctx: WorkspaceContext, logger: PluginLogger): Promise<void>;
11
+ export declare const CorrectionObserverService: CorrectionObserverServiceShape;
@@ -0,0 +1,172 @@
1
+ import { WorkspaceContext } from '../core/workspace-context.js';
2
+ import { TrajectoryRegistry } from '../core/trajectory.js';
3
+ import { CorrectionCueLearner } from '../core/correction-cue-learner.js';
4
+ import { WorkflowFunnelLoader, PiAiRuntimeAdapter, CorrectionObserver, AgentScheduler, } from '@principles/core/runtime-v2';
5
+ import { KeywordOptimizationService } from './keyword-optimization-service.js';
6
+ import { SystemLogger } from '../core/system-logger.js';
7
+ let correctionObserverTimeoutId = null;
8
+ let correctionObserverStopped = false;
9
+ const startedWorkspaces = new Set();
10
+ const CORRECTION_OBSERVER_INTERVAL_MS = 15 * 60 * 1000;
11
+ const CORRECTION_OBSERVER_INITIAL_DELAY_MS = 10_000;
12
+ const CORRECTION_OBSERVER_MAX_RECENT_SESSIONS = 20;
13
+ const CORRECTION_OBSERVER_MAX_PAYLOAD_SESSIONS = 5;
14
+ export function resolveCorrectionObserver(wctx, logger) {
15
+ try {
16
+ const loader = new WorkflowFunnelLoader(wctx.stateDir);
17
+ const funnel = loader.getFunnel('pd-correction-observer');
18
+ const policy = funnel?.policy;
19
+ if (!policy || policy.runtimeKind !== 'pi-ai') {
20
+ logger?.debug?.('[PD:CorrectionObserver] workflows.yaml pd-correction-observer policy not found. Falling back to environment variables.');
21
+ const provider = process.env.PD_CORRECTION_PROVIDER || 'anthropic';
22
+ const model = process.env.PD_CORRECTION_MODEL || 'anthropic/claude-3-5-sonnet';
23
+ const apiKeyEnv = process.env.PD_CORRECTION_API_KEY_ENV || 'ANTHROPIC_API_KEY';
24
+ const baseUrl = process.env.PD_CORRECTION_BASE_URL;
25
+ if (!process.env[apiKeyEnv]) {
26
+ logger?.debug?.(`[PD:CorrectionObserver] API key env ${apiKeyEnv} is not set. Periodic optimization disabled.`);
27
+ return null;
28
+ }
29
+ const adapter = new PiAiRuntimeAdapter({
30
+ provider,
31
+ model,
32
+ apiKeyEnv,
33
+ baseUrl,
34
+ workspace: wctx.workspaceDir,
35
+ });
36
+ return new CorrectionObserver({ runtimeAdapter: adapter });
37
+ }
38
+ const adapter = new PiAiRuntimeAdapter({
39
+ provider: String(policy.provider),
40
+ model: String(policy.model),
41
+ apiKeyEnv: String(policy.apiKeyEnv),
42
+ maxRetries: policy.maxRetries,
43
+ timeoutMs: policy.timeoutMs ?? 30_000,
44
+ baseUrl: policy.baseUrl,
45
+ workspace: wctx.workspaceDir,
46
+ });
47
+ return new CorrectionObserver({ runtimeAdapter: adapter }, { timeoutMs: policy.timeoutMs });
48
+ }
49
+ catch (err) {
50
+ logger?.warn?.(`[PD:CorrectionObserver] Failed to resolve CorrectionObserver: ${String(err)}`);
51
+ return null;
52
+ }
53
+ }
54
+ export async function runCorrectionObserverCycle(wctx, logger) {
55
+ try {
56
+ const observer = resolveCorrectionObserver(wctx, logger);
57
+ if (!observer) {
58
+ logger?.info?.('[PD:CorrectionObserver] Observer not resolved (no API key or config). Skipping cycle.');
59
+ return;
60
+ }
61
+ logger?.info?.('[PD:CorrectionObserver] Observer resolved. Initiating periodic optimization...');
62
+ const db = TrajectoryRegistry.get(wctx.workspaceDir);
63
+ const recentSessions = db.listRecentSessions({ limit: CORRECTION_OBSERVER_MAX_RECENT_SESSIONS });
64
+ const recentSessionIds = recentSessions.map(s => s.sessionId);
65
+ if (recentSessionIds.length === 0) {
66
+ logger?.info?.('[PD:CorrectionObserver] No recent sessions found. Skipping correction optimization.');
67
+ return;
68
+ }
69
+ const recentMessages = [];
70
+ for (const sId of recentSessionIds.slice(0, CORRECTION_OBSERVER_MAX_PAYLOAD_SESSIONS)) {
71
+ try {
72
+ const turns = db.listUserTurnsForSession(sId);
73
+ for (const t of turns) {
74
+ if (t.rawExcerpt) {
75
+ recentMessages.push(t.rawExcerpt);
76
+ }
77
+ }
78
+ }
79
+ catch (turnErr) {
80
+ logger?.warn?.(`[PD:CorrectionObserver] Failed to load user turns for session ${sId}: ${String(turnErr)}`);
81
+ }
82
+ }
83
+ const learner = CorrectionCueLearner.get(wctx.stateDir);
84
+ const keywords = learner.getStore().keywords;
85
+ const keywordStoreSummary = {
86
+ totalKeywords: keywords.length,
87
+ terms: keywords.map(k => ({
88
+ term: k.term,
89
+ weight: k.weight,
90
+ hitCount: k.hitCount ?? 0,
91
+ truePositiveCount: k.truePositiveCount ?? 0,
92
+ falsePositiveCount: k.falsePositiveCount ?? 0,
93
+ })),
94
+ };
95
+ const optimizationService = KeywordOptimizationService.get(wctx.stateDir, wctx.workspaceDir, logger);
96
+ const trajectoryHistory = await optimizationService.buildTrajectoryHistory(recentSessionIds);
97
+ const payload = {
98
+ parentSessionId: 'correction-observer-service',
99
+ workspaceDir: wctx.workspaceDir,
100
+ keywordStoreSummary,
101
+ recentMessages,
102
+ trajectoryHistory,
103
+ };
104
+ const scheduler = new AgentScheduler();
105
+ scheduler.register({
106
+ agentId: 'correction-observer',
107
+ mode: 'realtime',
108
+ runner: observer,
109
+ });
110
+ logger?.info?.(`[PD:CorrectionObserver] Dispatching with ${trajectoryHistory.length} trajectory events, ${recentMessages.length} recent messages.`);
111
+ const result = await scheduler.dispatch('correction-observer', payload);
112
+ logger?.info?.(`[PD:CorrectionObserver] Completed: updated=${result.updated}, summary="${result.summary}"`);
113
+ if (result.updated) {
114
+ optimizationService.applyResult(result);
115
+ }
116
+ }
117
+ catch (err) {
118
+ const errMsg = `Correction observer cycle failed: ${String(err)}`;
119
+ logger?.warn?.(`[PD:CorrectionObserver] ${errMsg}`);
120
+ SystemLogger.log(wctx.workspaceDir, 'CORRECTION_OBSERVER_CYCLE_FAILED', errMsg);
121
+ }
122
+ }
123
+ export const CorrectionObserverService = {
124
+ id: 'principles-correction-observer',
125
+ start(ctx) {
126
+ const workspaceDir = ctx?.workspaceDir;
127
+ const logger = ctx?.logger || console;
128
+ if (!workspaceDir) {
129
+ if (logger)
130
+ logger.warn('[PD:CorrectionObserver] workspaceDir not found in service config. Correction observer disabled.');
131
+ return;
132
+ }
133
+ if (startedWorkspaces.has(workspaceDir)) {
134
+ if (logger)
135
+ logger.info(`[PD:CorrectionObserver] Already started for workspace: ${workspaceDir}. Skipping duplicate start.`);
136
+ return;
137
+ }
138
+ startedWorkspaces.add(workspaceDir);
139
+ correctionObserverStopped = false;
140
+ const wctx = WorkspaceContext.fromHookContext({ workspaceDir, ...ctx.config });
141
+ if (logger)
142
+ logger.info(`[PD:CorrectionObserver] Starting with workspaceDir=${wctx.workspaceDir}, stateDir=${wctx.stateDir}`);
143
+ const interval = CORRECTION_OBSERVER_INTERVAL_MS;
144
+ async function runCycle() {
145
+ if (correctionObserverStopped)
146
+ return;
147
+ await runCorrectionObserverCycle(wctx, logger);
148
+ if (correctionObserverStopped)
149
+ return;
150
+ correctionObserverTimeoutId = setTimeout(runCycle, interval);
151
+ correctionObserverTimeoutId.unref();
152
+ }
153
+ correctionObserverTimeoutId = setTimeout(() => {
154
+ void runCycle().catch((err) => {
155
+ if (logger)
156
+ logger.error(`[PD:CorrectionObserver] Startup cycle failed: ${String(err)}`);
157
+ if (correctionObserverStopped)
158
+ return;
159
+ correctionObserverTimeoutId = setTimeout(runCycle, interval);
160
+ correctionObserverTimeoutId.unref();
161
+ });
162
+ }, CORRECTION_OBSERVER_INITIAL_DELAY_MS);
163
+ correctionObserverTimeoutId.unref();
164
+ },
165
+ stop(_ctx) {
166
+ correctionObserverStopped = true;
167
+ startedWorkspaces.clear();
168
+ if (correctionObserverTimeoutId)
169
+ clearTimeout(correctionObserverTimeoutId);
170
+ correctionObserverTimeoutId = null;
171
+ },
172
+ };