create-principles-disciple 1.79.0 → 1.81.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 (196) hide show
  1. package/console/package.json +1 -0
  2. package/core/dist/runtime-v2/__tests__/admission-gate.test.js +6 -6
  3. package/core/dist/runtime-v2/__tests__/admission-gate.test.js.map +1 -1
  4. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +39 -0
  5. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  6. package/core/dist/runtime-v2/__tests__/artificer-runner-vslice.test.js +52 -0
  7. package/core/dist/runtime-v2/__tests__/artificer-runner-vslice.test.js.map +1 -1
  8. package/core/dist/runtime-v2/__tests__/evaluator-runner-vslice.test.js +245 -1
  9. package/core/dist/runtime-v2/__tests__/evaluator-runner-vslice.test.js.map +1 -1
  10. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.js +131 -1
  11. package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.js.map +1 -1
  12. package/core/dist/runtime-v2/__tests__/fixtures/llm-e2e-config.d.ts.map +1 -1
  13. package/core/dist/runtime-v2/__tests__/fixtures/llm-e2e-config.js +4 -2
  14. package/core/dist/runtime-v2/__tests__/fixtures/llm-e2e-config.js.map +1 -1
  15. package/core/dist/runtime-v2/__tests__/scribe-runner-vslice.test.js +425 -2
  16. package/core/dist/runtime-v2/__tests__/scribe-runner-vslice.test.js.map +1 -1
  17. package/core/dist/runtime-v2/__tests__/telemetry-event.test.d.ts +2 -0
  18. package/core/dist/runtime-v2/__tests__/telemetry-event.test.d.ts.map +1 -0
  19. package/core/dist/runtime-v2/__tests__/telemetry-event.test.js +69 -0
  20. package/core/dist/runtime-v2/__tests__/telemetry-event.test.js.map +1 -0
  21. package/core/dist/runtime-v2/admission-gate.d.ts.map +1 -1
  22. package/core/dist/runtime-v2/admission-gate.js +0 -8
  23. package/core/dist/runtime-v2/admission-gate.js.map +1 -1
  24. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.d.ts +15 -0
  25. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.d.ts.map +1 -0
  26. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js +685 -0
  27. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js.map +1 -0
  28. package/core/dist/runtime-v2/config/index.d.ts +14 -0
  29. package/core/dist/runtime-v2/config/index.d.ts.map +1 -0
  30. package/core/dist/runtime-v2/config/index.js +16 -0
  31. package/core/dist/runtime-v2/config/index.js.map +1 -0
  32. package/core/dist/runtime-v2/config/pd-config-defaults.d.ts +14 -0
  33. package/core/dist/runtime-v2/config/pd-config-defaults.d.ts.map +1 -0
  34. package/core/dist/runtime-v2/config/pd-config-defaults.js +74 -0
  35. package/core/dist/runtime-v2/config/pd-config-defaults.js.map +1 -0
  36. package/core/dist/runtime-v2/config/pd-config-effective.d.ts +14 -0
  37. package/core/dist/runtime-v2/config/pd-config-effective.d.ts.map +1 -0
  38. package/core/dist/runtime-v2/config/pd-config-effective.js +118 -0
  39. package/core/dist/runtime-v2/config/pd-config-effective.js.map +1 -0
  40. package/core/dist/runtime-v2/config/pd-config-feature-flags.d.ts +35 -0
  41. package/core/dist/runtime-v2/config/pd-config-feature-flags.d.ts.map +1 -0
  42. package/core/dist/runtime-v2/config/pd-config-feature-flags.js +99 -0
  43. package/core/dist/runtime-v2/config/pd-config-feature-flags.js.map +1 -0
  44. package/core/dist/runtime-v2/config/pd-config-redaction.d.ts +22 -0
  45. package/core/dist/runtime-v2/config/pd-config-redaction.d.ts.map +1 -0
  46. package/core/dist/runtime-v2/config/pd-config-redaction.js +179 -0
  47. package/core/dist/runtime-v2/config/pd-config-redaction.js.map +1 -0
  48. package/core/dist/runtime-v2/config/pd-config-types.d.ts +124 -0
  49. package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -0
  50. package/core/dist/runtime-v2/config/pd-config-types.js +35 -0
  51. package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -0
  52. package/core/dist/runtime-v2/config/pd-config-validate.d.ts +16 -0
  53. package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -0
  54. package/core/dist/runtime-v2/config/pd-config-validate.js +443 -0
  55. package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -0
  56. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js +10 -10
  57. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js.map +1 -1
  58. package/core/dist/runtime-v2/feedback/index.d.ts +1 -1
  59. package/core/dist/runtime-v2/feedback/index.d.ts.map +1 -1
  60. package/core/dist/runtime-v2/feedback/index.js +1 -1
  61. package/core/dist/runtime-v2/feedback/index.js.map +1 -1
  62. package/core/dist/runtime-v2/feedback/redact-sensitive.d.ts +2 -0
  63. package/core/dist/runtime-v2/feedback/redact-sensitive.d.ts.map +1 -1
  64. package/core/dist/runtime-v2/feedback/redact-sensitive.js +32 -3
  65. package/core/dist/runtime-v2/feedback/redact-sensitive.js.map +1 -1
  66. package/core/dist/runtime-v2/index.d.ts +5 -1
  67. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  68. package/core/dist/runtime-v2/index.js +9 -1
  69. package/core/dist/runtime-v2/index.js.map +1 -1
  70. package/core/dist/runtime-v2/internalization/__tests__/peer-runner-lineage-injection.test.d.ts +2 -0
  71. package/core/dist/runtime-v2/internalization/__tests__/peer-runner-lineage-injection.test.d.ts.map +1 -0
  72. package/core/dist/runtime-v2/internalization/__tests__/peer-runner-lineage-injection.test.js +95 -0
  73. package/core/dist/runtime-v2/internalization/__tests__/peer-runner-lineage-injection.test.js.map +1 -0
  74. package/core/dist/runtime-v2/internalization/__tests__/philosopher-runner-trust-boundary.test.d.ts +2 -0
  75. package/core/dist/runtime-v2/internalization/__tests__/philosopher-runner-trust-boundary.test.d.ts.map +1 -0
  76. package/core/dist/runtime-v2/internalization/__tests__/philosopher-runner-trust-boundary.test.js +398 -0
  77. package/core/dist/runtime-v2/internalization/__tests__/philosopher-runner-trust-boundary.test.js.map +1 -0
  78. package/core/dist/runtime-v2/internalization/artificer-output.d.ts +3 -2
  79. package/core/dist/runtime-v2/internalization/artificer-output.d.ts.map +1 -1
  80. package/core/dist/runtime-v2/internalization/artificer-output.js +40 -31
  81. package/core/dist/runtime-v2/internalization/artificer-output.js.map +1 -1
  82. package/core/dist/runtime-v2/internalization/artificer-runner.d.ts +59 -38
  83. package/core/dist/runtime-v2/internalization/artificer-runner.d.ts.map +1 -1
  84. package/core/dist/runtime-v2/internalization/artificer-runner.js +122 -394
  85. package/core/dist/runtime-v2/internalization/artificer-runner.js.map +1 -1
  86. package/core/dist/runtime-v2/internalization/dreamer-output.d.ts +4 -3
  87. package/core/dist/runtime-v2/internalization/dreamer-output.d.ts.map +1 -1
  88. package/core/dist/runtime-v2/internalization/dreamer-output.js +11 -9
  89. package/core/dist/runtime-v2/internalization/dreamer-output.js.map +1 -1
  90. package/core/dist/runtime-v2/internalization/dreamer-runner.d.ts +31 -92
  91. package/core/dist/runtime-v2/internalization/dreamer-runner.d.ts.map +1 -1
  92. package/core/dist/runtime-v2/internalization/dreamer-runner.js +80 -492
  93. package/core/dist/runtime-v2/internalization/dreamer-runner.js.map +1 -1
  94. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +2 -2
  95. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
  96. package/core/dist/runtime-v2/internalization/evaluator-output.js +29 -23
  97. package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
  98. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +74 -48
  99. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  100. package/core/dist/runtime-v2/internalization/evaluator-runner.js +165 -443
  101. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  102. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +14 -0
  103. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
  104. package/core/dist/runtime-v2/internalization/peer-runner-contracts.js +21 -0
  105. package/core/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
  106. package/core/dist/runtime-v2/internalization/philosopher-output.d.ts +8 -2
  107. package/core/dist/runtime-v2/internalization/philosopher-output.d.ts.map +1 -1
  108. package/core/dist/runtime-v2/internalization/philosopher-output.js +16 -11
  109. package/core/dist/runtime-v2/internalization/philosopher-output.js.map +1 -1
  110. package/core/dist/runtime-v2/internalization/philosopher-runner.d.ts +36 -55
  111. package/core/dist/runtime-v2/internalization/philosopher-runner.d.ts.map +1 -1
  112. package/core/dist/runtime-v2/internalization/philosopher-runner.js +118 -400
  113. package/core/dist/runtime-v2/internalization/philosopher-runner.js.map +1 -1
  114. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts.map +1 -1
  115. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js +3 -1
  116. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js.map +1 -1
  117. package/core/dist/runtime-v2/internalization/scribe-output.d.ts +8 -2
  118. package/core/dist/runtime-v2/internalization/scribe-output.d.ts.map +1 -1
  119. package/core/dist/runtime-v2/internalization/scribe-output.js +33 -17
  120. package/core/dist/runtime-v2/internalization/scribe-output.js.map +1 -1
  121. package/core/dist/runtime-v2/internalization/scribe-runner.d.ts +56 -49
  122. package/core/dist/runtime-v2/internalization/scribe-runner.d.ts.map +1 -1
  123. package/core/dist/runtime-v2/internalization/scribe-runner.js +117 -397
  124. package/core/dist/runtime-v2/internalization/scribe-runner.js.map +1 -1
  125. package/core/dist/runtime-v2/internalization/trainer-runner.d.ts.map +1 -1
  126. package/core/dist/runtime-v2/internalization/trainer-runner.js +3 -1
  127. package/core/dist/runtime-v2/internalization/trainer-runner.js.map +1 -1
  128. package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-trust-boundary.test.d.ts +2 -0
  129. package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-trust-boundary.test.d.ts.map +1 -0
  130. package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-trust-boundary.test.js +379 -0
  131. package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-trust-boundary.test.js.map +1 -0
  132. package/core/dist/runtime-v2/runner/base-peer-runner.d.ts +122 -0
  133. package/core/dist/runtime-v2/runner/base-peer-runner.d.ts.map +1 -0
  134. package/core/dist/runtime-v2/runner/base-peer-runner.js +528 -0
  135. package/core/dist/runtime-v2/runner/base-peer-runner.js.map +1 -0
  136. package/core/dist/runtime-v2/runner/peer-runner-types.d.ts +107 -0
  137. package/core/dist/runtime-v2/runner/peer-runner-types.d.ts.map +1 -0
  138. package/core/dist/runtime-v2/runner/peer-runner-types.js +13 -0
  139. package/core/dist/runtime-v2/runner/peer-runner-types.js.map +1 -0
  140. package/core/dist/telemetry-event.d.ts +2 -2
  141. package/core/dist/telemetry-event.d.ts.map +1 -1
  142. package/core/dist/telemetry-event.js +89 -0
  143. package/core/dist/telemetry-event.js.map +1 -1
  144. package/dist/installer.d.ts +1 -1
  145. package/dist/installer.d.ts.map +1 -1
  146. package/dist/installer.js +39 -10
  147. package/dist/installer.js.map +1 -1
  148. package/dist/mvp-config.d.ts +33 -0
  149. package/dist/mvp-config.d.ts.map +1 -1
  150. package/dist/mvp-config.js +174 -0
  151. package/dist/mvp-config.js.map +1 -1
  152. package/package.json +3 -3
  153. package/pd-cli/dist/commands/config-doctor.d.ts +17 -0
  154. package/pd-cli/dist/commands/config-doctor.d.ts.map +1 -0
  155. package/pd-cli/dist/commands/config-doctor.js +143 -0
  156. package/pd-cli/dist/commands/config-doctor.js.map +1 -0
  157. package/pd-cli/dist/commands/console.d.ts +18 -0
  158. package/pd-cli/dist/commands/console.d.ts.map +1 -1
  159. package/pd-cli/dist/commands/console.js +439 -0
  160. package/pd-cli/dist/commands/console.js.map +1 -1
  161. package/pd-cli/dist/commands/runtime-features.d.ts +23 -8
  162. package/pd-cli/dist/commands/runtime-features.d.ts.map +1 -1
  163. package/pd-cli/dist/commands/runtime-features.js +72 -31
  164. package/pd-cli/dist/commands/runtime-features.js.map +1 -1
  165. package/pd-cli/dist/index.d.ts +1 -1
  166. package/pd-cli/dist/index.js +83 -31
  167. package/pd-cli/dist/index.js.map +1 -1
  168. package/pd-cli/dist/services/config-doctor.d.ts +102 -0
  169. package/pd-cli/dist/services/config-doctor.d.ts.map +1 -0
  170. package/pd-cli/dist/services/config-doctor.js +426 -0
  171. package/pd-cli/dist/services/config-doctor.js.map +1 -0
  172. package/pd-cli/dist/services/console-launcher.d.ts +110 -0
  173. package/pd-cli/dist/services/console-launcher.d.ts.map +1 -0
  174. package/pd-cli/dist/services/console-launcher.js +282 -0
  175. package/pd-cli/dist/services/console-launcher.js.map +1 -0
  176. package/pd-cli/dist/services/pd-config-loader.d.ts +64 -0
  177. package/pd-cli/dist/services/pd-config-loader.d.ts.map +1 -0
  178. package/pd-cli/dist/services/pd-config-loader.js +156 -0
  179. package/pd-cli/dist/services/pd-config-loader.js.map +1 -0
  180. package/pd-cli/package.json +3 -1
  181. package/plugin/dist/core/event-log.d.ts +10 -0
  182. package/plugin/dist/core/event-log.js +85 -1
  183. package/plugin/dist/core/pd-config-loader.d.ts +96 -0
  184. package/plugin/dist/core/pd-config-loader.js +307 -0
  185. package/plugin/dist/core/runtime-v2-prompt-activation-reader.d.ts +4 -0
  186. package/plugin/dist/core/runtime-v2-prompt-activation-reader.js +12 -56
  187. package/plugin/dist/core/surface-guard.d.ts +12 -8
  188. package/plugin/dist/core/surface-guard.js +51 -2
  189. package/plugin/dist/hooks/gate.js +3 -0
  190. package/plugin/dist/hooks/prompt.js +4 -5
  191. package/plugin/dist/index.d.ts +4 -0
  192. package/plugin/dist/index.js +70 -94
  193. package/plugin/dist/service/correction-observer-service.d.ts +8 -0
  194. package/plugin/dist/service/correction-observer-service.js +48 -29
  195. package/plugin/dist/utils/workspace-resolver.d.ts +33 -1
  196. package/plugin/dist/utils/workspace-resolver.js +182 -3
@@ -0,0 +1,307 @@
1
+ /**
2
+ * PD Config Loader (Plugin I/O boundary) — PRI-307
3
+ *
4
+ * Reads `.pd/config.yaml`, validates via core, computes effective config.
5
+ * Replaces the old `.pd/feature-flags.yaml` and `.state/workflows.yaml` reading
6
+ * for plugin production paths.
7
+ *
8
+ * ADR-0016: PD owns exactly one user config file.
9
+ * - Missing config → defaults with nextAction
10
+ * - Malformed config → fail loud with errors and nextAction
11
+ * - No secrets in output
12
+ * - Observer disabled → no start / no noisy log cycling
13
+ * - Observer enabled + missing setup → structured needs_setup + nextAction
14
+ */
15
+ import * as fs from 'fs';
16
+ import * as path from 'path';
17
+ import yaml from 'js-yaml';
18
+ import { validatePdConfig, computeEffectivePdConfig, computeFeatureFlagsFromConfig, INTERNAL_AGENT_NAMES, } from '@principles/core/runtime-v2';
19
+ // ── Constants ────────────────────────────────────────────────────────────────
20
+ export const PD_CONFIG_DIR = '.pd';
21
+ export const PD_CONFIG_FILENAME = 'config.yaml';
22
+ // ── Config Path ──────────────────────────────────────────────────────────────
23
+ export function getPdConfigPath(workspaceDir) {
24
+ return path.join(workspaceDir, PD_CONFIG_DIR, PD_CONFIG_FILENAME);
25
+ }
26
+ // ── Load PD Config ───────────────────────────────────────────────────────────
27
+ /**
28
+ * Load and validate `.pd/config.yaml` from the workspace.
29
+ * Never throws on malformed input. Always provides a usable fallback.
30
+ */
31
+ export function loadPdConfigForPlugin(workspaceDir) {
32
+ const configPath = getPdConfigPath(workspaceDir);
33
+ // 1) Config file missing → use defaults
34
+ if (!fs.existsSync(configPath)) {
35
+ const effective = computeEffectivePdConfig(null);
36
+ return {
37
+ ok: true,
38
+ effective,
39
+ source: 'defaults',
40
+ configPath,
41
+ warnings: effective.warnings,
42
+ errors: [],
43
+ };
44
+ }
45
+ // 2) Read the file
46
+ let raw;
47
+ try {
48
+ raw = fs.readFileSync(configPath, 'utf8');
49
+ }
50
+ catch (err) {
51
+ const message = err instanceof Error ? err.message : String(err);
52
+ const effective = computeEffectivePdConfig(null);
53
+ return {
54
+ ok: false,
55
+ effective,
56
+ source: 'malformed',
57
+ configPath,
58
+ warnings: [],
59
+ errors: [{ path: '', reason: `Failed to read .pd/config.yaml: ${message}`, nextAction: 'Check file permissions for .pd/config.yaml' }],
60
+ };
61
+ }
62
+ // 3) Parse YAML — treat as unknown (ERR-001)
63
+ let parsed;
64
+ try {
65
+ parsed = yaml.load(raw, { schema: yaml.JSON_SCHEMA });
66
+ }
67
+ catch (err) {
68
+ const message = err instanceof Error ? err.message : String(err);
69
+ const effective = computeEffectivePdConfig(null);
70
+ return {
71
+ ok: false,
72
+ effective,
73
+ source: 'malformed',
74
+ configPath,
75
+ warnings: [],
76
+ errors: [{ path: '', reason: `YAML parse error in .pd/config.yaml: ${message}`, nextAction: 'Fix YAML syntax in .pd/config.yaml' }],
77
+ };
78
+ }
79
+ // 4) Validate via core (ERR-001, ERR-005: no `as` bypasses)
80
+ const validationResult = validatePdConfig(parsed);
81
+ if (!validationResult.ok) {
82
+ const effective = computeEffectivePdConfig(null);
83
+ return {
84
+ ok: false,
85
+ effective,
86
+ source: 'malformed',
87
+ configPath,
88
+ warnings: [],
89
+ errors: validationResult.errors.map(e => ({
90
+ path: e.path,
91
+ reason: e.reason,
92
+ nextAction: e.nextAction,
93
+ })),
94
+ };
95
+ }
96
+ // 5) Compute effective config
97
+ const effective = computeEffectivePdConfig(validationResult.value);
98
+ return {
99
+ ok: true,
100
+ effective,
101
+ source: 'user_config',
102
+ configPath,
103
+ warnings: effective.warnings,
104
+ errors: [],
105
+ };
106
+ }
107
+ // ── Feature Flag from Config ─────────────────────────────────────────────────
108
+ /**
109
+ * Get a single feature flag's enabled state from .pd/config.yaml.
110
+ * Replaces the old `loadFeatureFlagFromWorkspace` which read .pd/feature-flags.yaml.
111
+ */
112
+ export function loadFeatureFlagFromConfig(workspaceDir, flagId, logger) {
113
+ const result = loadPdConfigForPlugin(workspaceDir);
114
+ const flags = computeFeatureFlagsFromConfig(result.effective);
115
+ const flag = flags.flags[flagId];
116
+ if (!result.ok) {
117
+ logger?.warn?.(`[PD:Config] Config validation failed: ${result.errors.map(e => e.reason).join('; ')} — using defaults`);
118
+ }
119
+ return {
120
+ enabled: flag?.enabled ?? false,
121
+ source: result.source,
122
+ };
123
+ }
124
+ // ── Observer Config Resolution ───────────────────────────────────────────────
125
+ /**
126
+ * Resolve observer configuration from .pd/config.yaml.
127
+ *
128
+ * Returns structured state:
129
+ * - config_malformed: config file is invalid — no guessing, fail loud
130
+ * - disabled: observer feature flag is off OR agent.enabled=false → no start, no noisy logs
131
+ * - needs_setup: observer enabled but runtime profile missing, API key not set, or unsupported profile type
132
+ * - ready: observer enabled and fully configured (pi-ai with key present)
133
+ * - not_ready: observer enabled, API key present, but runtime availability unknown
134
+ */
135
+ export function resolveObserverConfig(workspaceDir, observerFlagId, observerAgentName, _logger) {
136
+ const result = loadPdConfigForPlugin(workspaceDir);
137
+ // 0) Malformed config → fail loud, do NOT swallow as "disabled"
138
+ if (!result.ok) {
139
+ return {
140
+ enabled: false,
141
+ readiness: 'config_malformed',
142
+ source: 'malformed',
143
+ reason: `Config validation failed: ${result.errors.map(e => e.reason).join('; ')}`,
144
+ nextAction: result.errors[0]?.nextAction ?? 'Fix .pd/config.yaml and retry',
145
+ runtimeProfileId: null,
146
+ runtimeProfileType: null,
147
+ apiKeyEnv: null,
148
+ apiKeyPresent: false,
149
+ provider: null,
150
+ model: null,
151
+ timeoutMs: null,
152
+ baseUrl: null,
153
+ configErrors: result.errors,
154
+ };
155
+ }
156
+ const config = result.effective.config;
157
+ // 1) Check if the observer feature flag is enabled
158
+ const featureFlag = config.features[observerFlagId];
159
+ if (!featureFlag || !featureFlag.enabled) {
160
+ return {
161
+ enabled: false,
162
+ readiness: 'disabled',
163
+ source: result.source,
164
+ reason: `${observerFlagId} is disabled in .pd/config.yaml`,
165
+ nextAction: `Set features.${observerFlagId}.enabled=true in .pd/config.yaml to enable`,
166
+ runtimeProfileId: null,
167
+ runtimeProfileType: null,
168
+ apiKeyEnv: null,
169
+ apiKeyPresent: false,
170
+ provider: null,
171
+ model: null,
172
+ timeoutMs: null,
173
+ baseUrl: null,
174
+ };
175
+ }
176
+ // 2) Check if the agent itself is enabled (feature flag ≠ agent.enabled)
177
+ const knownNames = INTERNAL_AGENT_NAMES;
178
+ if (!knownNames.includes(observerAgentName)) {
179
+ return {
180
+ enabled: false,
181
+ readiness: 'needs_setup',
182
+ source: result.source,
183
+ reason: `Unknown agent name '${observerAgentName}'`,
184
+ nextAction: `Use one of the known agent names: ${INTERNAL_AGENT_NAMES.join(', ')}`,
185
+ runtimeProfileId: null,
186
+ runtimeProfileType: null,
187
+ apiKeyEnv: null,
188
+ apiKeyPresent: false,
189
+ provider: null,
190
+ model: null,
191
+ timeoutMs: null,
192
+ baseUrl: null,
193
+ };
194
+ }
195
+ const agentKey = observerAgentName;
196
+ const agentConfig = config.internalAgents.agents[agentKey];
197
+ // Feature flag on but agent.enabled=false → disabled (not enabled)
198
+ if (!agentConfig || !agentConfig.enabled) {
199
+ return {
200
+ enabled: false,
201
+ readiness: 'disabled',
202
+ source: result.source,
203
+ reason: `${observerFlagId} feature flag is enabled but internalAgents.agents.${observerAgentName}.enabled is false`,
204
+ nextAction: `Set internalAgents.agents.${observerAgentName}.enabled=true in .pd/config.yaml, or disable features.${observerFlagId}`,
205
+ runtimeProfileId: null,
206
+ runtimeProfileType: null,
207
+ apiKeyEnv: null,
208
+ apiKeyPresent: false,
209
+ provider: null,
210
+ model: null,
211
+ timeoutMs: null,
212
+ baseUrl: null,
213
+ };
214
+ }
215
+ // 3) Find the agent's runtime profile
216
+ const runtimeProfileId = agentConfig.runtimeProfile ?? config.internalAgents.defaultRuntime;
217
+ const profile = config.runtimeProfiles[runtimeProfileId];
218
+ if (!profile) {
219
+ return {
220
+ enabled: true,
221
+ readiness: 'needs_setup',
222
+ source: result.source,
223
+ reason: `Runtime profile '${runtimeProfileId}' not found in .pd/config.yaml`,
224
+ nextAction: `Add runtime profile '${runtimeProfileId}' to .pd/config.yaml runtimeProfiles`,
225
+ runtimeProfileId,
226
+ runtimeProfileType: null,
227
+ apiKeyEnv: null,
228
+ apiKeyPresent: false,
229
+ provider: null,
230
+ model: null,
231
+ timeoutMs: null,
232
+ baseUrl: null,
233
+ };
234
+ }
235
+ // 4) For pi-ai profiles, check API key
236
+ if (profile.type === 'pi-ai') {
237
+ const apiKeyEnv = profile.apiKeyEnv ?? null;
238
+ const apiKeyPresent = !!apiKeyEnv && Object.prototype.hasOwnProperty.call(process.env, apiKeyEnv) && !!process.env[apiKeyEnv];
239
+ if (!apiKeyEnv) {
240
+ return {
241
+ enabled: true,
242
+ readiness: 'needs_setup',
243
+ source: result.source,
244
+ reason: `pi-ai profile '${runtimeProfileId}' missing apiKeyEnv`,
245
+ nextAction: `Add apiKeyEnv to runtime profile '${runtimeProfileId}' in .pd/config.yaml`,
246
+ runtimeProfileId,
247
+ runtimeProfileType: profile.type,
248
+ apiKeyEnv: null,
249
+ apiKeyPresent: false,
250
+ provider: profile.provider ?? null,
251
+ model: profile.model ?? null,
252
+ timeoutMs: profile.timeoutMs ?? null,
253
+ baseUrl: profile.baseUrl ?? null,
254
+ };
255
+ }
256
+ if (!apiKeyPresent) {
257
+ return {
258
+ enabled: true,
259
+ readiness: 'needs_setup',
260
+ source: result.source,
261
+ reason: `Environment variable '${apiKeyEnv}' is not set or empty`,
262
+ nextAction: `Set the environment variable '${apiKeyEnv}' with a valid API key`,
263
+ runtimeProfileId,
264
+ runtimeProfileType: profile.type,
265
+ apiKeyEnv,
266
+ apiKeyPresent: false,
267
+ provider: profile.provider ?? null,
268
+ model: profile.model ?? null,
269
+ timeoutMs: profile.timeoutMs ?? null,
270
+ baseUrl: profile.baseUrl ?? null,
271
+ };
272
+ }
273
+ // pi-ai with key present — runtime availability unknown without actual probe
274
+ return {
275
+ enabled: true,
276
+ readiness: 'not_ready',
277
+ source: result.source,
278
+ reason: `pi-ai profile configured with apiKeyEnv='${apiKeyEnv}' (key present); runtime availability unknown`,
279
+ nextAction: 'Run pd runtime probe to verify end-to-end connectivity',
280
+ runtimeProfileId,
281
+ runtimeProfileType: profile.type,
282
+ apiKeyEnv,
283
+ apiKeyPresent: true,
284
+ provider: profile.provider ?? null,
285
+ model: profile.model ?? null,
286
+ timeoutMs: profile.timeoutMs ?? null,
287
+ baseUrl: profile.baseUrl ?? null,
288
+ };
289
+ }
290
+ // 5) OpenClaw profile — CorrectionObserver does NOT support OpenClaw runtime
291
+ // Mark as needs_setup with nextAction to configure a pi-ai profile
292
+ return {
293
+ enabled: true,
294
+ readiness: 'needs_setup',
295
+ source: result.source,
296
+ reason: `OpenClaw profile '${runtimeProfileId}' is not supported for observer runtime. Observers require a pi-ai profile with an API key.`,
297
+ nextAction: `Configure a pi-ai runtime profile for ${observerAgentName} in .pd/config.yaml (e.g., add a pi-ai profile with provider, model, and apiKeyEnv)`,
298
+ runtimeProfileId,
299
+ runtimeProfileType: profile.type,
300
+ apiKeyEnv: null,
301
+ apiKeyPresent: false,
302
+ provider: profile.provider ?? null,
303
+ model: profile.model ?? null,
304
+ timeoutMs: null,
305
+ baseUrl: null,
306
+ };
307
+ }
@@ -14,5 +14,9 @@ export declare class PromptActivationReader {
14
14
  constructor(workspaceDir: string, deps?: PromptActivationReaderDeps);
15
15
  readActivatedPrinciples(): Promise<PromptActivationReaderResult>;
16
16
  private queryArtifactRow;
17
+ /**
18
+ * PRI-305/PRI-307: Load feature flags from .pd/config.yaml instead of .pd/feature-flags.yaml.
19
+ * Uses the shared plugin config loader for consistency.
20
+ */
17
21
  private loadFeatureFlags;
18
22
  }
@@ -1,12 +1,6 @@
1
- import * as path from 'path';
2
- import * as fs from 'fs';
3
- import * as yaml from 'js-yaml';
4
- import { SqliteConnection, SqliteActivationStateStore, computeEffectiveFlags, DEFAULT_FEATURE_FLAGS, filterPromptActivations, resolvePrincipleFromArtifact } from '@principles/core/runtime-v2';
1
+ import { SqliteConnection, SqliteActivationStateStore, computeFeatureFlagsFromConfig, filterPromptActivations, resolvePrincipleFromArtifact } from '@principles/core/runtime-v2';
2
+ import { loadPdConfigForPlugin } from './pd-config-loader.js';
5
3
  export { RUNTIME_V2_PRINCIPLE_BUDGET } from '@principles/core/runtime-v2';
6
- const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
7
- function isRecord(value) {
8
- return value !== null && typeof value === 'object' && !Array.isArray(value);
9
- }
10
4
  export class PromptActivationReader {
11
5
  workspaceDir;
12
6
  deps;
@@ -88,56 +82,18 @@ export class PromptActivationReader {
88
82
  throw new Error(`artifact_query_failed: artifactId=${artifactId} reason=${msg}; nextAction=check_pi_artifacts_table`);
89
83
  }
90
84
  }
85
+ /**
86
+ * PRI-305/PRI-307: Load feature flags from .pd/config.yaml instead of .pd/feature-flags.yaml.
87
+ * Uses the shared plugin config loader for consistency.
88
+ */
91
89
  loadFeatureFlags() {
92
- const configPath = path.join(this.workspaceDir, '.pd', 'feature-flags.yaml');
93
- if (!fs.existsSync(configPath)) {
94
- return computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath);
95
- }
96
- let raw;
97
- try {
98
- raw = fs.readFileSync(configPath, 'utf8');
99
- }
100
- catch (e) {
101
- const msg = e instanceof Error ? e.message : String(e);
102
- this.deps.logger?.warn?.(`[PD:RuntimeV2] Feature flags unreadable: ${msg} — using defaults`);
103
- return computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath);
104
- }
105
- let parsed;
106
- try {
107
- parsed = yaml.load(raw, { schema: yaml.JSON_SCHEMA });
108
- }
109
- catch {
110
- this.deps.logger?.warn?.(`[PD:RuntimeV2] Feature flags YAML parse error — using defaults`);
111
- return {
112
- ...computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath),
113
- warnings: ['feature-flags.yaml: YAML parse error, using defaults'],
114
- };
115
- }
116
- if (!isRecord(parsed)) {
117
- this.deps.logger?.warn?.(`[PD:RuntimeV2] Feature flags not a mapping — using defaults`);
118
- return {
119
- ...computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath),
120
- warnings: ['feature-flags.yaml: expected a mapping, using defaults'],
121
- };
122
- }
123
- const parsedRecord = Object.create(null);
124
- const yamlWarnings = [];
125
- for (const key of Object.keys(parsed)) {
126
- if (DANGEROUS_KEYS.has(key)) {
127
- yamlWarnings.push(`feature-flags.yaml: dangerous key '${key}' rejected`);
128
- continue;
129
- }
130
- if (Object.hasOwn(parsed, key)) {
131
- parsedRecord[key] = parsed[key];
132
- }
133
- }
134
- const result = computeEffectiveFlags(parsedRecord, DEFAULT_FEATURE_FLAGS, configPath);
135
- if (yamlWarnings.length > 0) {
136
- result.warnings = [...yamlWarnings, ...result.warnings];
137
- for (const w of yamlWarnings) {
138
- this.deps.logger?.warn?.(`[PD:RuntimeV2] ${w}`);
90
+ const result = loadPdConfigForPlugin(this.workspaceDir);
91
+ const flags = computeFeatureFlagsFromConfig(result.effective);
92
+ if (!result.ok) {
93
+ for (const err of result.errors) {
94
+ this.deps.logger?.warn?.(`[PD:RuntimeV2] Config error at ${err.path}: ${err.reason}`);
139
95
  }
140
96
  }
141
- return result;
97
+ return flags;
142
98
  }
143
99
  }
@@ -7,6 +7,16 @@ export interface SurfaceGuardResult {
7
7
  violations: string[];
8
8
  warnings: string[];
9
9
  }
10
+ type LoggerLike = {
11
+ info?: (msg: string) => void;
12
+ debug?: (msg: string) => void;
13
+ };
14
+ /**
15
+ * Reset the per-process surface-guard skip log bookkeeping. Intended for tests
16
+ * that need to assert on the first-fire log without cross-test pollution.
17
+ * Not part of the production API surface; do not call from runtime code.
18
+ */
19
+ export declare function __resetSurfaceGuardSkipLogStateForTests(): void;
10
20
  export declare function checkSurfaceGuard(): SurfaceGuardResult;
11
21
  export declare function getSurfaceIdForHook(hookEvent: string, label?: string): string;
12
22
  export declare function getSurfaceIdForService(serviceName: string): string;
@@ -15,13 +25,7 @@ export declare function isSurfaceEnabled(surfaceId: string, overrides?: Record<s
15
25
  reason?: string;
16
26
  };
17
27
  export type HookHandler<E, C, R> = (event: E, ctx: C) => R | Promise<R>;
18
- export declare function guardHook<E, C, R>(surfaceId: string, logger: {
19
- info?: (msg: string) => void;
20
- debug?: (msg: string) => void;
21
- } | undefined, handler: HookHandler<E, C, R>): HookHandler<E, C, R>;
22
- export declare function guardService<T extends OpenClawPluginService>(surfaceId: string, service: T, logger?: {
23
- info?: (msg: string) => void;
24
- debug?: (msg: string) => void;
25
- }): T | null;
28
+ export declare function guardHook<E, C, R>(surfaceId: string, logger: LoggerLike | undefined, handler: HookHandler<E, C, R>): HookHandler<E, C, R>;
29
+ export declare function guardService<T extends OpenClawPluginService>(surfaceId: string, service: T, logger?: LoggerLike): T | null;
26
30
  export { PLUGIN_SURFACE_REGISTRY, validateSurfaceRegistry, getSurfacesByCategory };
27
31
  export type { PluginSurfaceEntry, MvpCategory };
@@ -1,4 +1,42 @@
1
1
  import { PLUGIN_SURFACE_REGISTRY, validateSurfaceRegistry, getSurfacesByCategory, } from '@principles/core/runtime-v2';
2
+ // Surface-level once-only log state (PRI-298).
3
+ // The first time a quiet/non-core surface guard actually fires in this
4
+ // process, the disabled reason is emitted once. Subsequent fires for the
5
+ // same surfaceId are still observable (the no-op handler preserves
6
+ // behaviour) but no longer flood the log. Fresh processes start with an
7
+ // empty set, so each plugin load gets one observable skip per surface.
8
+ //
9
+ // The Set is only updated when the log was actually emitted, so passing
10
+ // `undefined` for the logger on a quiet first fire does NOT consume the
11
+ // one-shot slot — a later registration that supplies a logger still gets
12
+ // the first-fire reason (PRI-298 / ERR-002).
13
+ const loggedSkipSurfaces = new Set();
14
+ /**
15
+ * Emit the disabled-reason log line for `surfaceId` at most once per
16
+ * process. Returns true if the log was emitted, false if it was suppressed
17
+ * (already logged, or no logger available). Only marks the surface as
18
+ * logged when the log was actually written, so a missing logger on first
19
+ * call does not consume the one-shot slot.
20
+ */
21
+ function logSkipOnce(surfaceId, logger, message) {
22
+ if (loggedSkipSurfaces.has(surfaceId)) {
23
+ return false;
24
+ }
25
+ if (!logger?.info) {
26
+ return false;
27
+ }
28
+ loggedSkipSurfaces.add(surfaceId);
29
+ logger.info(message);
30
+ return true;
31
+ }
32
+ /**
33
+ * Reset the per-process surface-guard skip log bookkeeping. Intended for tests
34
+ * that need to assert on the first-fire log without cross-test pollution.
35
+ * Not part of the production API surface; do not call from runtime code.
36
+ */
37
+ export function __resetSurfaceGuardSkipLogStateForTests() {
38
+ loggedSkipSurfaces.clear();
39
+ }
2
40
  export function checkSurfaceGuard() {
3
41
  const validation = validateSurfaceRegistry(PLUGIN_SURFACE_REGISTRY);
4
42
  const violations = [];
@@ -65,8 +103,15 @@ export function guardHook(surfaceId, logger, handler) {
65
103
  return handler;
66
104
  }
67
105
  const reason = check.reason ?? 'surface not enabled';
106
+ // Log on the first ACTUAL no-op invocation, not at construction time
107
+ // (PRI-298). Construction-time logging would emit a `SKIP` line at
108
+ // plugin startup for every registered quiet hook, regardless of
109
+ // whether the hook ever fires — which is exactly the startup log
110
+ // noise this change is meant to prevent. The one-shot is consumed only
111
+ // when the log was actually written, so `undefined` logger on first
112
+ // call does not eat the slot.
68
113
  return (_event, _ctx) => {
69
- logger?.info?.(`[PD:surface-guard] SKIP ${surfaceId}: ${reason}`);
114
+ logSkipOnce(surfaceId, logger, `[PD:surface-guard] SKIP ${surfaceId}: ${reason}`);
70
115
  return undefined;
71
116
  };
72
117
  }
@@ -76,7 +121,11 @@ export function guardService(surfaceId, service, logger) {
76
121
  return service;
77
122
  }
78
123
  const reason = check.reason ?? 'surface not enabled';
79
- logger?.info?.(`[PD:surface-guard] SKIP service ${surfaceId}: ${reason}`);
124
+ // guardService is called once per service during plugin registration,
125
+ // so the once-only check fires on the registration call itself. The
126
+ // shared helper makes the consumption rule identical to guardHook:
127
+ // a missing logger on first call does not consume the one-shot.
128
+ logSkipOnce(surfaceId, logger, `[PD:surface-guard] SKIP service ${surfaceId}: ${reason}`);
80
129
  return null;
81
130
  }
82
131
  export { PLUGIN_SURFACE_REGISTRY, validateSurfaceRegistry, getSurfacesByCategory };
@@ -316,6 +316,9 @@ export function handleBeforeToolCall(event, ctx) {
316
316
  // ---------------------------------------------------------------------------
317
317
  function _extractParamsSummary(params) {
318
318
  const summary = {};
319
+ // NOTE: Do NOT redact here — this feeds into RuleHost.evaluate() which
320
+ // may match against paramsSummary.command. Redaction happens at
321
+ // EventLog.record() before persistence.
319
322
  if (params.file_path)
320
323
  summary.file_path = params.file_path;
321
324
  if (params.path)
@@ -127,7 +127,7 @@ export function loadContextInjectionConfig(workspaceDir) {
127
127
  const raw = cachedReadFile(profilePath);
128
128
  if (raw) {
129
129
  const profile = JSON.parse(raw);
130
- if (profile.contextInjection) {
130
+ if (profile && typeof profile === 'object' && profile.contextInjection && typeof profile.contextInjection === 'object') {
131
131
  const contextInjection = profile.contextInjection;
132
132
  return {
133
133
  ...defaultContextConfig,
@@ -267,6 +267,9 @@ export async function handleBeforePromptBuild(event, ctx) {
267
267
  resetFriction(sessionId, workspaceDir);
268
268
  }
269
269
  // ──── 1. prependSystemContext: Minimal Agent Identity ────
270
+ // EvolutionWorker-era INTERNAL SYSTEM LAYOUT removed per PRI-294.
271
+ // The EVOLUTION_WORKER PathResolver key and system layout reference are
272
+ // not MVP-Core; agents discover what they need via tool calls.
270
273
  prependSystemContext = `## 【AGENT IDENTITY】
271
274
 
272
275
  You are a **self-evolving AI agent** powered by Principles Disciple.
@@ -284,10 +287,6 @@ You are a **self-evolving AI agent** powered by Principles Disciple.
284
287
  - Use the current session for the normal user reply.
285
288
  - Use sessions_send for cross-session messaging.
286
289
  - Use agents_list / sessions_list for peer-agent or peer-session orchestration.
287
-
288
- ## 🔧 INTERNAL SYSTEM LAYOUT
289
- - Your core plugin logic is rooted at: ${PathResolver.getExtensionRoot() || 'EXTENSION_ROOT (unresolved)'}
290
- - If you need self-inspection, prioritize the worker entry pointed by PathResolver key: EVOLUTION_WORKER
291
290
  `;
292
291
  // ──── 2. Empathy Observer Spawn (async sidecar)
293
292
  const empathySilenceConstraint = `
@@ -1,5 +1,9 @@
1
1
  import type { OpenClawPluginApi } from './openclaw-sdk.js';
2
2
  declare function isRecord(value: unknown): value is Record<string, unknown>;
3
+ /**
4
+ * PRI-305/PRI-307: Load feature flag from .pd/config.yaml instead of .pd/feature-flags.yaml.
5
+ * Delegates to the shared plugin config loader for consistency.
6
+ */
3
7
  declare function loadFeatureFlagFromWorkspace(workspaceDir: string, flagId: string, logger?: {
4
8
  warn?: (msg: string) => void;
5
9
  info?: (msg: string) => void;