create-principles-disciple 1.80.0 → 1.82.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.
- package/core/dist/runtime-v2/__tests__/admission-gate.test.js +6 -6
- package/core/dist/runtime-v2/__tests__/admission-gate.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +39 -0
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/artificer-runner-vslice.test.js +52 -0
- package/core/dist/runtime-v2/__tests__/artificer-runner-vslice.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/evaluator-runner-vslice.test.js +245 -1
- package/core/dist/runtime-v2/__tests__/evaluator-runner-vslice.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.js +131 -1
- package/core/dist/runtime-v2/__tests__/feedback/redact-sensitive.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/scribe-runner-vslice.test.js +425 -2
- package/core/dist/runtime-v2/__tests__/scribe-runner-vslice.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/telemetry-event.test.d.ts +2 -0
- package/core/dist/runtime-v2/__tests__/telemetry-event.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/__tests__/telemetry-event.test.js +69 -0
- package/core/dist/runtime-v2/__tests__/telemetry-event.test.js.map +1 -0
- package/core/dist/runtime-v2/admission-gate.d.ts.map +1 -1
- package/core/dist/runtime-v2/admission-gate.js +0 -8
- package/core/dist/runtime-v2/admission-gate.js.map +1 -1
- package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.d.ts +15 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js +685 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js.map +1 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-redaction-deep.test.d.ts +13 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-redaction-deep.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-redaction-deep.test.js +447 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-redaction-deep.test.js.map +1 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-validation-edge-cases.test.d.ts +12 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-validation-edge-cases.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-validation-edge-cases.test.js +433 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-validation-edge-cases.test.js.map +1 -0
- package/core/dist/runtime-v2/config/index.d.ts +14 -0
- package/core/dist/runtime-v2/config/index.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/index.js +16 -0
- package/core/dist/runtime-v2/config/index.js.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-defaults.d.ts +14 -0
- package/core/dist/runtime-v2/config/pd-config-defaults.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-defaults.js +74 -0
- package/core/dist/runtime-v2/config/pd-config-defaults.js.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-effective.d.ts +14 -0
- package/core/dist/runtime-v2/config/pd-config-effective.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-effective.js +118 -0
- package/core/dist/runtime-v2/config/pd-config-effective.js.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-feature-flags.d.ts +35 -0
- package/core/dist/runtime-v2/config/pd-config-feature-flags.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-feature-flags.js +99 -0
- package/core/dist/runtime-v2/config/pd-config-feature-flags.js.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-redaction.d.ts +22 -0
- package/core/dist/runtime-v2/config/pd-config-redaction.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-redaction.js +179 -0
- package/core/dist/runtime-v2/config/pd-config-redaction.js.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-types.d.ts +124 -0
- package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-types.js +35 -0
- package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-validate.d.ts +16 -0
- package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -0
- package/core/dist/runtime-v2/config/pd-config-validate.js +443 -0
- package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -0
- package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js +10 -10
- package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js.map +1 -1
- package/core/dist/runtime-v2/feedback/index.d.ts +1 -1
- package/core/dist/runtime-v2/feedback/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/feedback/index.js +1 -1
- package/core/dist/runtime-v2/feedback/index.js.map +1 -1
- package/core/dist/runtime-v2/feedback/redact-sensitive.d.ts +2 -0
- package/core/dist/runtime-v2/feedback/redact-sensitive.d.ts.map +1 -1
- package/core/dist/runtime-v2/feedback/redact-sensitive.js +32 -3
- package/core/dist/runtime-v2/feedback/redact-sensitive.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +5 -1
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +9 -1
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/philosopher-runner-trust-boundary.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/philosopher-runner-trust-boundary.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/philosopher-runner-trust-boundary.test.js +398 -0
- package/core/dist/runtime-v2/internalization/__tests__/philosopher-runner-trust-boundary.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/artificer-output.d.ts +3 -2
- package/core/dist/runtime-v2/internalization/artificer-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-output.js +40 -31
- package/core/dist/runtime-v2/internalization/artificer-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-runner.d.ts +59 -38
- package/core/dist/runtime-v2/internalization/artificer-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-runner.js +121 -395
- package/core/dist/runtime-v2/internalization/artificer-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/dreamer-output.d.ts +4 -3
- package/core/dist/runtime-v2/internalization/dreamer-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/dreamer-output.js +11 -9
- package/core/dist/runtime-v2/internalization/dreamer-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/dreamer-runner.d.ts +31 -92
- package/core/dist/runtime-v2/internalization/dreamer-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/dreamer-runner.js +79 -495
- package/core/dist/runtime-v2/internalization/dreamer-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +2 -2
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-output.js +29 -23
- package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +74 -48
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +164 -444
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/philosopher-output.d.ts +8 -2
- package/core/dist/runtime-v2/internalization/philosopher-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/philosopher-output.js +16 -11
- package/core/dist/runtime-v2/internalization/philosopher-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/philosopher-runner.d.ts +36 -55
- package/core/dist/runtime-v2/internalization/philosopher-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/philosopher-runner.js +117 -401
- package/core/dist/runtime-v2/internalization/philosopher-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-output.d.ts +8 -2
- package/core/dist/runtime-v2/internalization/scribe-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-output.js +33 -17
- package/core/dist/runtime-v2/internalization/scribe-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-runner.d.ts +56 -49
- package/core/dist/runtime-v2/internalization/scribe-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-runner.js +116 -398
- package/core/dist/runtime-v2/internalization/scribe-runner.js.map +1 -1
- package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-trust-boundary.test.d.ts +2 -0
- package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-trust-boundary.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-trust-boundary.test.js +379 -0
- package/core/dist/runtime-v2/runner/__tests__/base-peer-runner-trust-boundary.test.js.map +1 -0
- package/core/dist/runtime-v2/runner/base-peer-runner.d.ts +122 -0
- package/core/dist/runtime-v2/runner/base-peer-runner.d.ts.map +1 -0
- package/core/dist/runtime-v2/runner/base-peer-runner.js +528 -0
- package/core/dist/runtime-v2/runner/base-peer-runner.js.map +1 -0
- package/core/dist/runtime-v2/runner/peer-runner-types.d.ts +107 -0
- package/core/dist/runtime-v2/runner/peer-runner-types.d.ts.map +1 -0
- package/core/dist/runtime-v2/runner/peer-runner-types.js +13 -0
- package/core/dist/runtime-v2/runner/peer-runner-types.js.map +1 -0
- package/core/dist/telemetry-event.d.ts +2 -2
- package/core/dist/telemetry-event.d.ts.map +1 -1
- package/core/dist/telemetry-event.js +89 -0
- package/core/dist/telemetry-event.js.map +1 -1
- package/dist/installer.d.ts +1 -1
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +24 -10
- package/dist/installer.js.map +1 -1
- package/dist/mvp-config.d.ts +33 -0
- package/dist/mvp-config.d.ts.map +1 -1
- package/dist/mvp-config.js +174 -0
- package/dist/mvp-config.js.map +1 -1
- package/package.json +1 -1
- package/pd-cli/dist/commands/config-doctor.d.ts +17 -0
- package/pd-cli/dist/commands/config-doctor.d.ts.map +1 -0
- package/pd-cli/dist/commands/config-doctor.js +143 -0
- package/pd-cli/dist/commands/config-doctor.js.map +1 -0
- package/pd-cli/dist/commands/console.d.ts +18 -0
- package/pd-cli/dist/commands/console.d.ts.map +1 -1
- package/pd-cli/dist/commands/console.js +439 -0
- package/pd-cli/dist/commands/console.js.map +1 -1
- package/pd-cli/dist/commands/runtime-features.d.ts +23 -8
- package/pd-cli/dist/commands/runtime-features.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-features.js +72 -31
- package/pd-cli/dist/commands/runtime-features.js.map +1 -1
- package/pd-cli/dist/index.d.ts +1 -1
- package/pd-cli/dist/index.js +83 -31
- package/pd-cli/dist/index.js.map +1 -1
- package/pd-cli/dist/services/config-doctor.d.ts +102 -0
- package/pd-cli/dist/services/config-doctor.d.ts.map +1 -0
- package/pd-cli/dist/services/config-doctor.js +426 -0
- package/pd-cli/dist/services/config-doctor.js.map +1 -0
- package/pd-cli/dist/services/console-launcher.d.ts +110 -0
- package/pd-cli/dist/services/console-launcher.d.ts.map +1 -0
- package/pd-cli/dist/services/console-launcher.js +282 -0
- package/pd-cli/dist/services/console-launcher.js.map +1 -0
- package/pd-cli/dist/services/pd-config-loader.d.ts +64 -0
- package/pd-cli/dist/services/pd-config-loader.d.ts.map +1 -0
- package/pd-cli/dist/services/pd-config-loader.js +156 -0
- package/pd-cli/dist/services/pd-config-loader.js.map +1 -0
- package/pd-cli/package.json +3 -1
- package/plugin/dist/core/event-log.d.ts +10 -0
- package/plugin/dist/core/event-log.js +85 -1
- package/plugin/dist/core/pd-config-loader.d.ts +96 -0
- package/plugin/dist/core/pd-config-loader.js +307 -0
- package/plugin/dist/core/runtime-v2-prompt-activation-reader.d.ts +4 -0
- package/plugin/dist/core/runtime-v2-prompt-activation-reader.js +12 -56
- package/plugin/dist/core/surface-guard.d.ts +12 -8
- package/plugin/dist/core/surface-guard.js +51 -2
- package/plugin/dist/hooks/gate.js +3 -0
- package/plugin/dist/index.d.ts +4 -0
- package/plugin/dist/index.js +8 -52
- package/plugin/dist/service/correction-observer-service.d.ts +8 -0
- package/plugin/dist/service/correction-observer-service.js +48 -29
|
@@ -2,6 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { createEmptyDailyStats } from '../types/event-types.js';
|
|
4
4
|
import { atomicWriteFileSync } from '../utils/io.js';
|
|
5
|
+
import { redactTelemetryString } from '@principles/core/runtime-v2';
|
|
5
6
|
const EVENT_LOG_RETENTION_DAYS = 7;
|
|
6
7
|
export class EventLog {
|
|
7
8
|
logsDir;
|
|
@@ -142,16 +143,99 @@ export class EventLog {
|
|
|
142
143
|
recordRuntimeV2ActivationsInjected(data) {
|
|
143
144
|
this.record('runtime_v2_prompt_activations_injected', 'injected', data.sessionId, data);
|
|
144
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Redact telemetry-sensitive string values in event data before persistence.
|
|
148
|
+
* Applies redactTelemetryString to known high-risk fields (filePath, command,
|
|
149
|
+
* reason, args, new_string, old_string, text, paramsSummary values) and to all
|
|
150
|
+
* string values in tool_call/rulehost_* data as a safety net.
|
|
151
|
+
*
|
|
152
|
+
* ERR-002: never throws; returns data unchanged on error.
|
|
153
|
+
* ERR-045/046: covers composite command strings, Authorization headers, env vars.
|
|
154
|
+
*/
|
|
155
|
+
redactEventData(type, data) {
|
|
156
|
+
try {
|
|
157
|
+
// Known high-risk event types — telemetry that carries tool commands / paths
|
|
158
|
+
const telemetryTypes = new Set([
|
|
159
|
+
'tool_call',
|
|
160
|
+
'rulehost_evaluated',
|
|
161
|
+
'rulehost_blocked',
|
|
162
|
+
'rulehost_requireApproval',
|
|
163
|
+
'rulehost_auto_correct_proposed',
|
|
164
|
+
'rulehost_auto_correct_applied',
|
|
165
|
+
'rule_enforced',
|
|
166
|
+
'hook_execution',
|
|
167
|
+
'gate_block',
|
|
168
|
+
'gate_bypass',
|
|
169
|
+
]);
|
|
170
|
+
if (!telemetryTypes.has(type))
|
|
171
|
+
return data;
|
|
172
|
+
const redacted = {};
|
|
173
|
+
for (const [key, value] of Object.entries(data)) {
|
|
174
|
+
if (typeof value === 'string') {
|
|
175
|
+
redacted[key] = redactTelemetryString(value);
|
|
176
|
+
}
|
|
177
|
+
else if (Array.isArray(value)) {
|
|
178
|
+
// Recurse into arrays (e.g. correctedFields with original/applied)
|
|
179
|
+
redacted[key] = value.map((item) => {
|
|
180
|
+
if (typeof item === 'string') {
|
|
181
|
+
return redactTelemetryString(item);
|
|
182
|
+
}
|
|
183
|
+
if (typeof item === 'object' && item !== null) {
|
|
184
|
+
const nested = {};
|
|
185
|
+
for (const [nk, nv] of Object.entries(item)) {
|
|
186
|
+
nested[nk] = typeof nv === 'string' ? redactTelemetryString(nv) : nv;
|
|
187
|
+
}
|
|
188
|
+
return nested;
|
|
189
|
+
}
|
|
190
|
+
return item;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
else if (typeof value === 'object' && value !== null) {
|
|
194
|
+
// Recurse one level for nested objects (e.g. paramsSummary)
|
|
195
|
+
const nested = {};
|
|
196
|
+
for (const [nk, nv] of Object.entries(value)) {
|
|
197
|
+
if (typeof nv === 'string') {
|
|
198
|
+
nested[nk] = redactTelemetryString(nv);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
nested[nk] = nv;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
redacted[key] = nested;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
redacted[key] = value;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return redacted;
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
// ERR-002: fail safe — never write raw payload on redaction failure.
|
|
214
|
+
// Return a masked payload with context so downstream knows what happened.
|
|
215
|
+
const errStr = e instanceof Error ? e.message.slice(0, 200) : String(e).slice(0, 200);
|
|
216
|
+
const masked = {
|
|
217
|
+
redactionFailure: true,
|
|
218
|
+
redactionStatus: 'failed',
|
|
219
|
+
'redaction.status': 'failed',
|
|
220
|
+
redactionReason: errStr || 'unknown error',
|
|
221
|
+
redactionDataDropped: true,
|
|
222
|
+
originalType: type,
|
|
223
|
+
originalSessionId: data.sessionId ?? null,
|
|
224
|
+
};
|
|
225
|
+
return masked;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
145
228
|
record(type, category, sessionId, data) {
|
|
146
229
|
const now = new Date();
|
|
147
230
|
const date = this.formatDate(now);
|
|
231
|
+
const redactedData = this.redactEventData(type, data);
|
|
148
232
|
const entry = {
|
|
149
233
|
ts: now.toISOString(),
|
|
150
234
|
date,
|
|
151
235
|
type,
|
|
152
236
|
category,
|
|
153
237
|
sessionId,
|
|
154
|
-
data:
|
|
238
|
+
data: redactedData,
|
|
155
239
|
};
|
|
156
240
|
this.eventBuffer.push(entry);
|
|
157
241
|
this.updateStats(entry);
|
|
@@ -0,0 +1,96 @@
|
|
|
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 type { EffectivePdConfig } from '@principles/core/runtime-v2';
|
|
16
|
+
export declare const PD_CONFIG_DIR = ".pd";
|
|
17
|
+
export declare const PD_CONFIG_FILENAME = "config.yaml";
|
|
18
|
+
export type ObserverReadiness = 'disabled' | 'needs_setup' | 'ready' | 'not_ready' | 'config_malformed';
|
|
19
|
+
export interface ObserverConfigResult {
|
|
20
|
+
/** Whether the observer feature is enabled in config */
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
/** Observer readiness state */
|
|
23
|
+
readiness: ObserverReadiness;
|
|
24
|
+
/** Config source: 'defaults' | 'user_config' | 'malformed' */
|
|
25
|
+
source: string;
|
|
26
|
+
/** Reason for current state */
|
|
27
|
+
reason: string;
|
|
28
|
+
/** What the user should do next */
|
|
29
|
+
nextAction: string;
|
|
30
|
+
/** The runtime profile ID for this observer, if configured */
|
|
31
|
+
runtimeProfileId: string | null;
|
|
32
|
+
/** The runtime profile type, if configured */
|
|
33
|
+
runtimeProfileType: string | null;
|
|
34
|
+
/** The apiKeyEnv for the runtime profile, if applicable */
|
|
35
|
+
apiKeyEnv: string | null;
|
|
36
|
+
/** Whether the apiKeyEnv is present in process.env */
|
|
37
|
+
apiKeyPresent: boolean;
|
|
38
|
+
/** Provider name from runtime profile */
|
|
39
|
+
provider: string | null;
|
|
40
|
+
/** Model name from runtime profile */
|
|
41
|
+
model: string | null;
|
|
42
|
+
/** Timeout from runtime profile */
|
|
43
|
+
timeoutMs: number | null;
|
|
44
|
+
/** Base URL from runtime profile */
|
|
45
|
+
baseUrl: string | null;
|
|
46
|
+
/** Config validation errors (only present when readiness=config_malformed) */
|
|
47
|
+
configErrors?: Array<{
|
|
48
|
+
path: string;
|
|
49
|
+
reason: string;
|
|
50
|
+
nextAction: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
export interface PluginConfigLoadResult {
|
|
54
|
+
ok: boolean;
|
|
55
|
+
effective: EffectivePdConfig;
|
|
56
|
+
source: 'defaults' | 'user_config' | 'malformed';
|
|
57
|
+
configPath: string;
|
|
58
|
+
warnings: string[];
|
|
59
|
+
errors: Array<{
|
|
60
|
+
path: string;
|
|
61
|
+
reason: string;
|
|
62
|
+
nextAction: string;
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
65
|
+
export declare function getPdConfigPath(workspaceDir: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Load and validate `.pd/config.yaml` from the workspace.
|
|
68
|
+
* Never throws on malformed input. Always provides a usable fallback.
|
|
69
|
+
*/
|
|
70
|
+
export declare function loadPdConfigForPlugin(workspaceDir: string): PluginConfigLoadResult;
|
|
71
|
+
/**
|
|
72
|
+
* Get a single feature flag's enabled state from .pd/config.yaml.
|
|
73
|
+
* Replaces the old `loadFeatureFlagFromWorkspace` which read .pd/feature-flags.yaml.
|
|
74
|
+
*/
|
|
75
|
+
export declare function loadFeatureFlagFromConfig(workspaceDir: string, flagId: string, logger?: {
|
|
76
|
+
warn?: (msg: string) => void;
|
|
77
|
+
info?: (msg: string) => void;
|
|
78
|
+
}): {
|
|
79
|
+
enabled: boolean;
|
|
80
|
+
source: string;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Resolve observer configuration from .pd/config.yaml.
|
|
84
|
+
*
|
|
85
|
+
* Returns structured state:
|
|
86
|
+
* - config_malformed: config file is invalid — no guessing, fail loud
|
|
87
|
+
* - disabled: observer feature flag is off OR agent.enabled=false → no start, no noisy logs
|
|
88
|
+
* - needs_setup: observer enabled but runtime profile missing, API key not set, or unsupported profile type
|
|
89
|
+
* - ready: observer enabled and fully configured (pi-ai with key present)
|
|
90
|
+
* - not_ready: observer enabled, API key present, but runtime availability unknown
|
|
91
|
+
*/
|
|
92
|
+
export declare function resolveObserverConfig(workspaceDir: string, observerFlagId: string, observerAgentName: string, _logger?: {
|
|
93
|
+
warn?: (msg: string) => void;
|
|
94
|
+
info?: (msg: string) => void;
|
|
95
|
+
debug?: (msg: string) => void;
|
|
96
|
+
}): ObserverConfigResult;
|
|
@@ -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
|
|
2
|
-
import
|
|
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
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
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
|
-
|
|
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 };
|