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.
- package/console/package.json +1 -0
- 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__/fixtures/llm-e2e-config.d.ts.map +1 -1
- package/core/dist/runtime-v2/__tests__/fixtures/llm-e2e-config.js +4 -2
- package/core/dist/runtime-v2/__tests__/fixtures/llm-e2e-config.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/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__/peer-runner-lineage-injection.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/peer-runner-lineage-injection.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/peer-runner-lineage-injection.test.js +95 -0
- package/core/dist/runtime-v2/internalization/__tests__/peer-runner-lineage-injection.test.js.map +1 -0
- 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 +122 -394
- 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 +80 -492
- 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 +165 -443
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +14 -0
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.js +21 -0
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.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 +118 -400
- package/core/dist/runtime-v2/internalization/philosopher-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js +3 -1
- package/core/dist/runtime-v2/internalization/rollout-reviewer-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 +117 -397
- package/core/dist/runtime-v2/internalization/scribe-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/trainer-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/trainer-runner.js +3 -1
- package/core/dist/runtime-v2/internalization/trainer-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 +39 -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 +3 -3
- 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/hooks/prompt.js +4 -5
- package/plugin/dist/index.d.ts +4 -0
- package/plugin/dist/index.js +70 -94
- package/plugin/dist/service/correction-observer-service.d.ts +8 -0
- package/plugin/dist/service/correction-observer-service.js +48 -29
- package/plugin/dist/utils/workspace-resolver.d.ts +33 -1
- package/plugin/dist/utils/workspace-resolver.js +182 -3
package/plugin/dist/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
-
import
|
|
3
|
-
import * as yaml from 'js-yaml';
|
|
4
|
-
import { computeEffectiveFlags, DEFAULT_FEATURE_FLAGS } from '@principles/core/runtime-v2';
|
|
2
|
+
import { loadFeatureFlagFromConfig } from './core/pd-config-loader.js';
|
|
5
3
|
import { classifyTask } from './core/local-worker-routing.js';
|
|
6
4
|
import { completeShadowObservation, recordShadowRouting } from './core/shadow-observation-registry.js';
|
|
7
5
|
import { getCommandDescription } from './i18n/commands.js';
|
|
@@ -39,72 +37,28 @@ import { migrateDirectoryStructure } from './core/migration.js';
|
|
|
39
37
|
import { migrateStaleWorkspaceGuidance } from './core/workspace-guidance-migrator.js';
|
|
40
38
|
import { SystemLogger } from './core/system-logger.js';
|
|
41
39
|
import { PathResolver } from './core/path-resolver.js';
|
|
42
|
-
import { resolveCommandWorkspaceDir, resolveToolHookWorkspaceDirSafe } from './utils/workspace-resolver.js';
|
|
40
|
+
import { resolveCommandWorkspaceDir, resolveToolHookWorkspaceDirSafe, resolveHookWorkspaceDir } from './utils/workspace-resolver.js';
|
|
43
41
|
import { computeRuntimeShadowTaskFingerprint, PD_LOCAL_PROFILES } from './utils/shadow-fingerprint.js';
|
|
44
42
|
import { validateWorkspaceDir } from './core/workspace-dir-validation.js';
|
|
45
43
|
import { resolveWorkspaceDirFromApi } from './core/path-resolver.js';
|
|
46
44
|
import { checkSurfaceGuard, guardHook, guardService } from './core/surface-guard.js';
|
|
47
45
|
// Track started workspaces — one-time init + evolution worker per workspace
|
|
48
46
|
const startedWorkspaces = new Set();
|
|
49
|
-
const HOOK_WORKSPACE_RESOLUTION_NEXT_ACTION = 'verify gateway plugin activation and hook workspace binding; ' +
|
|
50
|
-
'migrate live hook workspace resolution to PD-owned canonical configuration before relying on config-based recovery';
|
|
51
47
|
// Map from childSessionKey → shadowObservationId
|
|
52
48
|
// Used to complete shadow observations when subagent ends
|
|
53
49
|
const pendingShadowObservations = new Map();
|
|
54
50
|
// ── Feature Flag Loader (plugin I/O boundary) ─────────────────────────────
|
|
55
51
|
// Reads workspace feature-flags.yaml and checks a specific flag.
|
|
56
52
|
// Returns the flag definition with effective enabled state.
|
|
57
|
-
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
58
53
|
function isRecord(value) {
|
|
59
54
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
60
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* PRI-305/PRI-307: Load feature flag from .pd/config.yaml instead of .pd/feature-flags.yaml.
|
|
58
|
+
* Delegates to the shared plugin config loader for consistency.
|
|
59
|
+
*/
|
|
61
60
|
function loadFeatureFlagFromWorkspace(workspaceDir, flagId, logger) {
|
|
62
|
-
|
|
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 };
|
|
61
|
+
return loadFeatureFlagFromConfig(workspaceDir, flagId, logger);
|
|
108
62
|
}
|
|
109
63
|
export function shouldStartEvolutionWorker(workspaceDir, logger) {
|
|
110
64
|
const flag = loadFeatureFlagFromWorkspace(workspaceDir, 'evolution_worker', logger);
|
|
@@ -113,7 +67,7 @@ export function shouldStartEvolutionWorker(workspaceDir, logger) {
|
|
|
113
67
|
}
|
|
114
68
|
const disabledInfo = JSON.stringify({
|
|
115
69
|
reason: 'mvp_quiet_per_adr0014',
|
|
116
|
-
nextAction: 'set evolution_worker.enabled=true in .pd/
|
|
70
|
+
nextAction: 'set features.evolution_worker.enabled=true in .pd/config.yaml to enable',
|
|
117
71
|
featureFlag: 'evolution_worker',
|
|
118
72
|
boundedContext: 'legacy_evolution_worker',
|
|
119
73
|
flagSource: flag.source,
|
|
@@ -127,7 +81,7 @@ export function shouldStartCorrectionObserver(workspaceDir, logger) {
|
|
|
127
81
|
}
|
|
128
82
|
const disabledInfo = JSON.stringify({
|
|
129
83
|
reason: 'correction_observer_disabled',
|
|
130
|
-
nextAction: 'set correction_observer.enabled=true in .pd/
|
|
84
|
+
nextAction: 'set features.correction_observer.enabled=true in .pd/config.yaml to enable',
|
|
131
85
|
featureFlag: 'correction_observer',
|
|
132
86
|
boundedContext: 'correction_observer_service',
|
|
133
87
|
flagSource: flag.source,
|
|
@@ -170,15 +124,18 @@ const plugin = {
|
|
|
170
124
|
const language = api.pluginConfig?.language || 'en';
|
|
171
125
|
// ── Hook: Prompt Building ──
|
|
172
126
|
api.on('before_prompt_build', guardHook('hook:before_prompt_build', api.logger, async (event, ctx) => {
|
|
173
|
-
const
|
|
174
|
-
if (!
|
|
127
|
+
const wsResult = resolveHookWorkspaceDir(ctx, api, 'before_prompt_build');
|
|
128
|
+
if (!wsResult.ok) {
|
|
175
129
|
api.logger.error(`[PD:before_prompt_build] workspaceDir resolution failed. ` +
|
|
176
|
-
`agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'} ` +
|
|
177
|
-
`sessionKey=${ctx.sessionKey ?? '(missing)'}. ` +
|
|
130
|
+
`reason=${wsResult.reason} agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
178
131
|
`Hook skipped — no mutation will occur. ` +
|
|
179
|
-
`NextAction: ${
|
|
132
|
+
`NextAction: ${wsResult.nextAction}`);
|
|
180
133
|
return;
|
|
181
134
|
}
|
|
135
|
+
const workspaceDir = wsResult.workspaceDir;
|
|
136
|
+
if (wsResult.consistencyWarning) {
|
|
137
|
+
api.logger.warn(`[PD:before_prompt_build] ${wsResult.consistencyWarning}`);
|
|
138
|
+
}
|
|
182
139
|
try {
|
|
183
140
|
if (!startedWorkspaces.has(workspaceDir)) {
|
|
184
141
|
startedWorkspaces.add(workspaceDir);
|
|
@@ -240,15 +197,18 @@ const plugin = {
|
|
|
240
197
|
}));
|
|
241
198
|
// ── Hook: Security Gate ──
|
|
242
199
|
api.on('before_tool_call', guardHook('hook:before_tool_call', api.logger, (event, ctx) => {
|
|
243
|
-
const
|
|
244
|
-
if (!
|
|
200
|
+
const wsResult = resolveHookWorkspaceDir(ctx, api, 'before_tool_call');
|
|
201
|
+
if (!wsResult.ok) {
|
|
245
202
|
api.logger.error(`[PD:before_tool_call] workspaceDir resolution failed. ` +
|
|
246
|
-
`agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'} ` +
|
|
247
|
-
`sessionKey=${ctx.sessionKey ?? '(missing)'}. ` +
|
|
203
|
+
`reason=${wsResult.reason} agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
248
204
|
`Hook skipped — security gate bypassed. ` +
|
|
249
|
-
`NextAction: ${
|
|
205
|
+
`NextAction: ${wsResult.nextAction}`);
|
|
250
206
|
return;
|
|
251
207
|
}
|
|
208
|
+
const workspaceDir = wsResult.workspaceDir;
|
|
209
|
+
if (wsResult.consistencyWarning) {
|
|
210
|
+
api.logger.warn(`[PD:before_tool_call] ${wsResult.consistencyWarning}`);
|
|
211
|
+
}
|
|
252
212
|
try {
|
|
253
213
|
const pluginConfig = api.pluginConfig ?? {};
|
|
254
214
|
const { logger } = api;
|
|
@@ -268,15 +228,18 @@ const plugin = {
|
|
|
268
228
|
}));
|
|
269
229
|
// ── Hook: Pain & Trust ──
|
|
270
230
|
api.on('after_tool_call', guardHook('hook:after_tool_call', api.logger, (event, ctx) => {
|
|
271
|
-
const
|
|
272
|
-
if (!
|
|
231
|
+
const wsResult = resolveHookWorkspaceDir(ctx, api, 'after_tool_call');
|
|
232
|
+
if (!wsResult.ok) {
|
|
273
233
|
api.logger.error(`[PD:after_tool_call] workspaceDir resolution failed. ` +
|
|
274
|
-
`agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'} ` +
|
|
275
|
-
`sessionKey=${ctx.sessionKey ?? '(missing)'}. ` +
|
|
234
|
+
`reason=${wsResult.reason} agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
276
235
|
`Hook skipped — pain detection bypassed. ` +
|
|
277
|
-
`NextAction: ${
|
|
236
|
+
`NextAction: ${wsResult.nextAction}`);
|
|
278
237
|
return;
|
|
279
238
|
}
|
|
239
|
+
const workspaceDir = wsResult.workspaceDir;
|
|
240
|
+
if (wsResult.consistencyWarning) {
|
|
241
|
+
api.logger.warn(`[PD:after_tool_call] ${wsResult.consistencyWarning}`);
|
|
242
|
+
}
|
|
280
243
|
try {
|
|
281
244
|
const pluginConfig = api.pluginConfig ?? {};
|
|
282
245
|
// Pass api separately to handleAfterToolCall to maintain type safety
|
|
@@ -295,15 +258,19 @@ const plugin = {
|
|
|
295
258
|
}));
|
|
296
259
|
// ── Hook: LLM Analysis ──
|
|
297
260
|
api.on('llm_output', guardHook('hook:llm_output', api.logger, (event, ctx) => {
|
|
298
|
-
const
|
|
299
|
-
if (!
|
|
261
|
+
const wsResult = resolveHookWorkspaceDir(ctx, api, 'llm_output');
|
|
262
|
+
if (!wsResult.ok) {
|
|
300
263
|
api.logger.error(`[PD:llm_output] workspaceDir resolution failed. ` +
|
|
301
|
-
`agentId=${ctx.agentId ?? '(missing)'} ` +
|
|
264
|
+
`reason=${wsResult.reason} agentId=${ctx.agentId ?? '(missing)'} ` +
|
|
302
265
|
`sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
303
266
|
`Hook skipped — LLM analysis bypassed. ` +
|
|
304
|
-
`NextAction: ${
|
|
267
|
+
`NextAction: ${wsResult.nextAction}`);
|
|
305
268
|
return;
|
|
306
269
|
}
|
|
270
|
+
const workspaceDir = wsResult.workspaceDir;
|
|
271
|
+
if (wsResult.consistencyWarning) {
|
|
272
|
+
api.logger.warn(`[PD:llm_output] ${wsResult.consistencyWarning}`);
|
|
273
|
+
}
|
|
307
274
|
try {
|
|
308
275
|
handleLlmOutput(event, { ...ctx, workspaceDir });
|
|
309
276
|
WorkspaceContext.fromHookContext({ workspaceDir }).eventLog.recordHookExecution({
|
|
@@ -423,41 +390,50 @@ const plugin = {
|
|
|
423
390
|
}));
|
|
424
391
|
// ── Hook: Lifecycle ──
|
|
425
392
|
api.on('before_reset', guardHook('hook:before_reset', api.logger, (event, ctx) => {
|
|
426
|
-
const
|
|
427
|
-
if (!
|
|
393
|
+
const wsResult = resolveHookWorkspaceDir(ctx, api, 'before_reset');
|
|
394
|
+
if (!wsResult.ok) {
|
|
428
395
|
api.logger.error(`[PD:before_reset] workspaceDir resolution failed. ` +
|
|
429
|
-
`agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
430
|
-
`Hook skipped. NextAction: ${
|
|
396
|
+
`reason=${wsResult.reason} agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
397
|
+
`Hook skipped. NextAction: ${wsResult.nextAction}`);
|
|
431
398
|
return;
|
|
432
399
|
}
|
|
433
|
-
|
|
400
|
+
if (wsResult.consistencyWarning) {
|
|
401
|
+
api.logger.warn(`[PD:before_reset] ${wsResult.consistencyWarning}`);
|
|
402
|
+
}
|
|
403
|
+
return handleBeforeReset(event, { ...ctx, workspaceDir: wsResult.workspaceDir });
|
|
434
404
|
}));
|
|
435
405
|
api.on('before_compaction', guardHook('hook:before_compaction', api.logger, (event, ctx) => {
|
|
436
|
-
const
|
|
437
|
-
if (!
|
|
406
|
+
const wsResult = resolveHookWorkspaceDir(ctx, api, 'before_compaction');
|
|
407
|
+
if (!wsResult.ok) {
|
|
438
408
|
api.logger.error(`[PD:before_compaction] workspaceDir resolution failed. ` +
|
|
439
|
-
`agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
440
|
-
`Hook skipped. NextAction: ${
|
|
409
|
+
`reason=${wsResult.reason} agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
410
|
+
`Hook skipped. NextAction: ${wsResult.nextAction}`);
|
|
441
411
|
return;
|
|
442
412
|
}
|
|
443
|
-
|
|
413
|
+
if (wsResult.consistencyWarning) {
|
|
414
|
+
api.logger.warn(`[PD:before_compaction] ${wsResult.consistencyWarning}`);
|
|
415
|
+
}
|
|
416
|
+
return handleBeforeCompaction(event, { ...ctx, workspaceDir: wsResult.workspaceDir });
|
|
444
417
|
}));
|
|
445
418
|
api.on('after_compaction', guardHook('hook:after_compaction', api.logger, (event, ctx) => {
|
|
446
|
-
const
|
|
447
|
-
if (!
|
|
419
|
+
const wsResult = resolveHookWorkspaceDir(ctx, api, 'after_compaction');
|
|
420
|
+
if (!wsResult.ok) {
|
|
448
421
|
api.logger.error(`[PD:after_compaction] workspaceDir resolution failed. ` +
|
|
449
|
-
`agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
450
|
-
`Hook skipped. NextAction: ${
|
|
422
|
+
`reason=${wsResult.reason} agentId=${ctx.agentId ?? '(missing)'} sessionId=${ctx.sessionId ?? '(missing)'}. ` +
|
|
423
|
+
`Hook skipped. NextAction: ${wsResult.nextAction}`);
|
|
451
424
|
return;
|
|
452
425
|
}
|
|
453
|
-
|
|
426
|
+
if (wsResult.consistencyWarning) {
|
|
427
|
+
api.logger.warn(`[PD:after_compaction] ${wsResult.consistencyWarning}`);
|
|
428
|
+
}
|
|
429
|
+
return handleAfterCompaction(event, { ...ctx, workspaceDir: wsResult.workspaceDir });
|
|
454
430
|
}));
|
|
455
|
-
// ── Service
|
|
431
|
+
// ── Service Registration (surface-guarded) ──
|
|
432
|
+
// PRI-294: EvolutionWorker service registration removed — it starts via
|
|
433
|
+
// before_prompt_build hook gate, not via api.registerService. The surface
|
|
434
|
+
// guard already prevents registration when disabled (enabledByDefault=false).
|
|
435
|
+
// Dead pre-assignment of EvolutionWorkerService.api removed.
|
|
456
436
|
try {
|
|
457
|
-
EvolutionWorkerService.api = api;
|
|
458
|
-
const guardedEvolutionWorker = guardService('service:evolution-worker', EvolutionWorkerService, api.logger);
|
|
459
|
-
if (guardedEvolutionWorker)
|
|
460
|
-
api.registerService(guardedEvolutionWorker);
|
|
461
437
|
const guardedCorrectionObserver = guardService('service:correction-observer', CorrectionObserverService, api.logger);
|
|
462
438
|
if (guardedCorrectionObserver)
|
|
463
439
|
api.registerService(guardedCorrectionObserver);
|
|
@@ -6,6 +6,14 @@ export interface CorrectionObserverServiceShape {
|
|
|
6
6
|
start: (ctx: OpenClawPluginServiceContext) => void;
|
|
7
7
|
stop?: (ctx: OpenClawPluginServiceContext) => void;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* PRI-307: Resolve CorrectionObserver from .pd/config.yaml.
|
|
11
|
+
*
|
|
12
|
+
* States:
|
|
13
|
+
* - disabled: feature flag off → return null, no noisy logs
|
|
14
|
+
* - needs_setup: enabled but missing API key or profile → return null with structured reason
|
|
15
|
+
* - ready/not_ready: enabled and configured → return observer instance
|
|
16
|
+
*/
|
|
9
17
|
export declare function resolveCorrectionObserver(wctx: WorkspaceContext, logger?: Pick<PluginLogger, 'info' | 'warn' | 'error' | 'debug'>): CorrectionObserver | null;
|
|
10
18
|
export declare function runCorrectionObserverCycle(wctx: WorkspaceContext, logger: PluginLogger): Promise<void>;
|
|
11
19
|
export declare const CorrectionObserverService: CorrectionObserverServiceShape;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
2
2
|
import { TrajectoryRegistry } from '../core/trajectory.js';
|
|
3
3
|
import { CorrectionCueLearner } from '../core/correction-cue-learner.js';
|
|
4
|
-
import {
|
|
4
|
+
import { PiAiRuntimeAdapter, CorrectionObserver, AgentScheduler, } from '@principles/core/runtime-v2';
|
|
5
5
|
import { KeywordOptimizationService } from './keyword-optimization-service.js';
|
|
6
6
|
import { SystemLogger } from '../core/system-logger.js';
|
|
7
|
+
import { resolveObserverConfig } from '../core/pd-config-loader.js';
|
|
7
8
|
let correctionObserverTimeoutId = null;
|
|
8
9
|
let correctionObserverStopped = false;
|
|
9
10
|
const startedWorkspaces = new Set();
|
|
@@ -11,40 +12,45 @@ const CORRECTION_OBSERVER_INTERVAL_MS = 15 * 60 * 1000;
|
|
|
11
12
|
const CORRECTION_OBSERVER_INITIAL_DELAY_MS = 10_000;
|
|
12
13
|
const CORRECTION_OBSERVER_MAX_RECENT_SESSIONS = 20;
|
|
13
14
|
const CORRECTION_OBSERVER_MAX_PAYLOAD_SESSIONS = 5;
|
|
15
|
+
/**
|
|
16
|
+
* PRI-307: Resolve CorrectionObserver from .pd/config.yaml.
|
|
17
|
+
*
|
|
18
|
+
* States:
|
|
19
|
+
* - disabled: feature flag off → return null, no noisy logs
|
|
20
|
+
* - needs_setup: enabled but missing API key or profile → return null with structured reason
|
|
21
|
+
* - ready/not_ready: enabled and configured → return observer instance
|
|
22
|
+
*/
|
|
14
23
|
export function resolveCorrectionObserver(wctx, logger) {
|
|
15
24
|
try {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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;
|
|
25
|
+
const observerConfig = resolveObserverConfig(wctx.workspaceDir, 'correction_observer', 'correctionObserver', logger);
|
|
26
|
+
if (!observerConfig.enabled) {
|
|
27
|
+
if (observerConfig.readiness === 'config_malformed') {
|
|
28
|
+
logger?.warn?.(`[PD:CorrectionObserver] Config malformed: ${observerConfig.reason}. ${observerConfig.nextAction}`);
|
|
28
29
|
}
|
|
30
|
+
else {
|
|
31
|
+
logger?.debug?.(`[PD:CorrectionObserver] ${observerConfig.reason}`);
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (observerConfig.readiness === 'needs_setup') {
|
|
36
|
+
logger?.info?.(`[PD:CorrectionObserver] ${observerConfig.reason}. ${observerConfig.nextAction}`);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
// ready or not_ready — create the observer
|
|
40
|
+
if (observerConfig.runtimeProfileType === 'pi-ai') {
|
|
29
41
|
const adapter = new PiAiRuntimeAdapter({
|
|
30
|
-
provider,
|
|
31
|
-
model,
|
|
32
|
-
apiKeyEnv,
|
|
33
|
-
|
|
42
|
+
provider: observerConfig.provider ?? 'anthropic',
|
|
43
|
+
model: observerConfig.model ?? 'anthropic/claude-3-5-sonnet',
|
|
44
|
+
apiKeyEnv: observerConfig.apiKeyEnv ?? 'ANTHROPIC_API_KEY',
|
|
45
|
+
timeoutMs: observerConfig.timeoutMs ?? undefined,
|
|
46
|
+
baseUrl: observerConfig.baseUrl ?? undefined,
|
|
34
47
|
workspace: wctx.workspaceDir,
|
|
35
48
|
});
|
|
36
|
-
return new CorrectionObserver({ runtimeAdapter: adapter });
|
|
49
|
+
return new CorrectionObserver({ runtimeAdapter: adapter }, { timeoutMs: observerConfig.timeoutMs ?? undefined });
|
|
37
50
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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 });
|
|
51
|
+
// OpenClaw profile — not yet supported for observer runtime
|
|
52
|
+
logger?.info?.(`[PD:CorrectionObserver] OpenClaw runtime profile not yet supported for correction observer. Skipping.`);
|
|
53
|
+
return null;
|
|
48
54
|
}
|
|
49
55
|
catch (err) {
|
|
50
56
|
logger?.warn?.(`[PD:CorrectionObserver] Failed to resolve CorrectionObserver: ${String(err)}`);
|
|
@@ -55,7 +61,8 @@ export async function runCorrectionObserverCycle(wctx, logger) {
|
|
|
55
61
|
try {
|
|
56
62
|
const observer = resolveCorrectionObserver(wctx, logger);
|
|
57
63
|
if (!observer) {
|
|
58
|
-
|
|
64
|
+
// PRI-307: No noisy "no API key" cycling. Only log at debug level.
|
|
65
|
+
logger?.debug?.(`[PD:CorrectionObserver] Observer not resolved. Skipping cycle.`);
|
|
59
66
|
return;
|
|
60
67
|
}
|
|
61
68
|
logger?.info?.('[PD:CorrectionObserver] Observer resolved. Initiating periodic optimization...');
|
|
@@ -135,6 +142,18 @@ export const CorrectionObserverService = {
|
|
|
135
142
|
logger.info(`[PD:CorrectionObserver] Already started for workspace: ${workspaceDir}. Skipping duplicate start.`);
|
|
136
143
|
return;
|
|
137
144
|
}
|
|
145
|
+
// PRI-307: Check observer config before starting
|
|
146
|
+
const observerConfig = resolveObserverConfig(workspaceDir, 'correction_observer', 'correctionObserver', logger);
|
|
147
|
+
if (!observerConfig.enabled) {
|
|
148
|
+
// Disabled → no start, no noisy cycling. Single structured log.
|
|
149
|
+
logger?.info?.(`[PD:CorrectionObserver] ${observerConfig.reason}. ${observerConfig.nextAction}`);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (observerConfig.readiness === 'needs_setup') {
|
|
153
|
+
// Enabled but missing setup → structured needs_setup, no noisy cycling
|
|
154
|
+
logger?.info?.(`[PD:CorrectionObserver] ${observerConfig.reason}. ${observerConfig.nextAction}`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
138
157
|
startedWorkspaces.add(workspaceDir);
|
|
139
158
|
correctionObserverStopped = false;
|
|
140
159
|
const wctx = WorkspaceContext.fromHookContext({ workspaceDir, ...ctx.config });
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* Workspace Directory Resolution Utilities
|
|
3
3
|
*
|
|
4
4
|
* Shared helpers for resolving workspace directories across commands and hooks.
|
|
5
|
+
*
|
|
6
|
+
* Hook resolution priority (PRI-259): PD canonical config → OpenClaw fallback.
|
|
7
|
+
* PD canonical sources: PD_WORKSPACE_DIR env → OPENCLAW_WORKSPACE env →
|
|
8
|
+
* principles-disciple.json → ~/.openclaw/workspace default.
|
|
9
|
+
* OpenClaw fallback: ctx.workspaceDir → api.runtime.agent.resolveAgentWorkspaceDir().
|
|
5
10
|
*/
|
|
6
11
|
import type { OpenClawPluginApi, PluginCommandContext } from '../openclaw-sdk.js';
|
|
7
12
|
import { type WorkspaceResolutionContext } from '../core/workspace-dir-validation.js';
|
|
@@ -29,11 +34,38 @@ export declare function resolveCommandWorkspaceDir(api: OpenClawPluginApi, ctx:
|
|
|
29
34
|
* @param source - Source label for error messages (e.g. 'evolution-status', 'pain')
|
|
30
35
|
*/
|
|
31
36
|
export declare function resolvePluginCommandWorkspaceDir(ctx: PluginCommandContext, source: string): string;
|
|
37
|
+
export type CanonicalWorkspaceSource = 'pd_env' | 'openclaw_env' | 'pd_config' | 'pd_default';
|
|
38
|
+
export interface CanonicalWorkspaceResult {
|
|
39
|
+
workspaceDir: string;
|
|
40
|
+
source: CanonicalWorkspaceSource;
|
|
41
|
+
}
|
|
42
|
+
export declare function resolveCanonicalWorkspaceDir(): CanonicalWorkspaceResult | null;
|
|
43
|
+
export type HookWorkspaceSource = CanonicalWorkspaceSource | 'openclaw_context' | 'openclaw_api';
|
|
44
|
+
export interface HookWorkspaceResolutionSuccess {
|
|
45
|
+
ok: true;
|
|
46
|
+
workspaceDir: string;
|
|
47
|
+
source: HookWorkspaceSource;
|
|
48
|
+
consistencyWarning?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface HookWorkspaceResolutionFailure {
|
|
51
|
+
ok: false;
|
|
52
|
+
reason: string;
|
|
53
|
+
nextAction: string;
|
|
54
|
+
message: string;
|
|
55
|
+
}
|
|
56
|
+
export type HookWorkspaceResolutionResult = HookWorkspaceResolutionSuccess | HookWorkspaceResolutionFailure;
|
|
57
|
+
export interface HookWorkspaceResolutionOptions {
|
|
58
|
+
canonicalResolver?: () => CanonicalWorkspaceResult | null;
|
|
59
|
+
explicitPdResolver?: () => CanonicalWorkspaceResult | null;
|
|
60
|
+
}
|
|
61
|
+
export declare function resolveHookWorkspaceDir(ctx: WorkspaceResolutionContext, api: OpenClawPluginApi, source: string, options?: HookWorkspaceResolutionOptions): HookWorkspaceResolutionResult;
|
|
32
62
|
/**
|
|
33
63
|
* Resolve workspace directory for tool hook execution (safe version).
|
|
34
64
|
* Returns undefined instead of throwing if resolution fails.
|
|
65
|
+
*
|
|
66
|
+
* PRI-259: Uses PD canonical config as primary source, OpenClaw as fallback.
|
|
35
67
|
*/
|
|
36
|
-
export declare function resolveToolHookWorkspaceDirSafe(ctx: WorkspaceResolutionContext, api: OpenClawPluginApi, source: string): string | undefined;
|
|
68
|
+
export declare function resolveToolHookWorkspaceDirSafe(ctx: WorkspaceResolutionContext, api: OpenClawPluginApi, source: string, options?: HookWorkspaceResolutionOptions): string | undefined;
|
|
37
69
|
export declare class WorkspaceResolutionError extends Error {
|
|
38
70
|
readonly reason: string;
|
|
39
71
|
readonly nextAction: string;
|
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
* Workspace Directory Resolution Utilities
|
|
3
3
|
*
|
|
4
4
|
* Shared helpers for resolving workspace directories across commands and hooks.
|
|
5
|
+
*
|
|
6
|
+
* Hook resolution priority (PRI-259): PD canonical config → OpenClaw fallback.
|
|
7
|
+
* PD canonical sources: PD_WORKSPACE_DIR env → OPENCLAW_WORKSPACE env →
|
|
8
|
+
* principles-disciple.json → ~/.openclaw/workspace default.
|
|
9
|
+
* OpenClaw fallback: ctx.workspaceDir → api.runtime.agent.resolveAgentWorkspaceDir().
|
|
5
10
|
*/
|
|
6
11
|
import { validateWorkspaceDir } from '../core/workspace-dir-validation.js';
|
|
7
|
-
import { resolveWorkspaceDir } from '../core/workspace-dir-service.js';
|
|
8
12
|
import { resolveWorkspaceDirFromApi } from '../core/path-resolver.js';
|
|
9
13
|
import * as path from 'path';
|
|
14
|
+
import * as os from 'os';
|
|
15
|
+
import * as fs from 'fs';
|
|
10
16
|
/**
|
|
11
17
|
* Resolve workspace directory for command execution.
|
|
12
18
|
*
|
|
@@ -69,12 +75,185 @@ export function resolvePluginCommandWorkspaceDir(ctx, source) {
|
|
|
69
75
|
throw new Error(`[PD:Command:${source}] CRITICAL: workspaceDir is not set in ctx.workspaceDir or ctx.config.workspaceDir. ` +
|
|
70
76
|
`Commands cannot execute without a valid workspace. Set OPENCLAW_WORKSPACE_DIR env var or ensure the workspace is properly initialized.`);
|
|
71
77
|
}
|
|
78
|
+
const PD_CONFIG_FILENAME = 'principles-disciple.json';
|
|
79
|
+
function loadWorkspaceFromPdConfigFile() {
|
|
80
|
+
const candidates = [
|
|
81
|
+
path.join(os.homedir(), '.openclaw', PD_CONFIG_FILENAME),
|
|
82
|
+
path.join(os.homedir(), '.principles', PD_CONFIG_FILENAME),
|
|
83
|
+
path.join(process.cwd(), PD_CONFIG_FILENAME),
|
|
84
|
+
];
|
|
85
|
+
for (const configPath of candidates) {
|
|
86
|
+
if (!fs.existsSync(configPath))
|
|
87
|
+
continue;
|
|
88
|
+
try {
|
|
89
|
+
const raw = fs.readFileSync(configPath, 'utf8');
|
|
90
|
+
const parsed = JSON.parse(raw);
|
|
91
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
92
|
+
if (Object.hasOwn(parsed, 'workspace')) {
|
|
93
|
+
const workspaceValue = parsed['workspace'];
|
|
94
|
+
if (typeof workspaceValue === 'string' && workspaceValue.trim()) {
|
|
95
|
+
return workspaceValue.trim();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
export function resolveCanonicalWorkspaceDir() {
|
|
107
|
+
const pdEnv = process.env.PD_WORKSPACE_DIR;
|
|
108
|
+
if (pdEnv && pdEnv.trim()) {
|
|
109
|
+
const dir = path.resolve(pdEnv.trim());
|
|
110
|
+
if (!validateWorkspaceDir(dir)) {
|
|
111
|
+
return { workspaceDir: dir, source: 'pd_env' };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const ocEnv = process.env.OPENCLAW_WORKSPACE;
|
|
115
|
+
if (ocEnv && ocEnv.trim()) {
|
|
116
|
+
const dir = path.resolve(ocEnv.trim());
|
|
117
|
+
if (!validateWorkspaceDir(dir)) {
|
|
118
|
+
return { workspaceDir: dir, source: 'openclaw_env' };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const configWorkspace = loadWorkspaceFromPdConfigFile();
|
|
122
|
+
if (configWorkspace) {
|
|
123
|
+
const dir = path.resolve(configWorkspace);
|
|
124
|
+
if (!validateWorkspaceDir(dir)) {
|
|
125
|
+
return { workspaceDir: dir, source: 'pd_config' };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const defaultDir = path.join(os.homedir(), '.openclaw', 'workspace');
|
|
129
|
+
if (!validateWorkspaceDir(defaultDir)) {
|
|
130
|
+
return { workspaceDir: defaultDir, source: 'pd_default' };
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Resolve only PD explicit sources (env vars + config file), excluding pd_default.
|
|
136
|
+
* Used by hook resolution to ensure ctx.workspaceDir takes priority over the
|
|
137
|
+
* hardcoded default fallback.
|
|
138
|
+
*/
|
|
139
|
+
function resolveExplicitPdSources() {
|
|
140
|
+
const pdEnv = process.env.PD_WORKSPACE_DIR;
|
|
141
|
+
if (pdEnv && pdEnv.trim()) {
|
|
142
|
+
const dir = path.resolve(pdEnv.trim());
|
|
143
|
+
if (!validateWorkspaceDir(dir)) {
|
|
144
|
+
return { workspaceDir: dir, source: 'pd_env' };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const ocEnv = process.env.OPENCLAW_WORKSPACE;
|
|
148
|
+
if (ocEnv && ocEnv.trim()) {
|
|
149
|
+
const dir = path.resolve(ocEnv.trim());
|
|
150
|
+
if (!validateWorkspaceDir(dir)) {
|
|
151
|
+
return { workspaceDir: dir, source: 'openclaw_env' };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const configWorkspace = loadWorkspaceFromPdConfigFile();
|
|
155
|
+
if (configWorkspace) {
|
|
156
|
+
const dir = path.resolve(configWorkspace);
|
|
157
|
+
if (!validateWorkspaceDir(dir)) {
|
|
158
|
+
return { workspaceDir: dir, source: 'pd_config' };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
function tryResolveFromOpenClawApi(api, agentId) {
|
|
164
|
+
try {
|
|
165
|
+
const resolved = api.runtime?.agent?.resolveAgentWorkspaceDir?.(api.config, agentId ?? 'main');
|
|
166
|
+
if (resolved && !validateWorkspaceDir(resolved)) {
|
|
167
|
+
return resolved;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// Fall through
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
export function resolveHookWorkspaceDir(ctx, api, source, options) {
|
|
176
|
+
// Priority 1: PD explicit sources (env vars + config file) — these are
|
|
177
|
+
// owner-declared and intentionally override the live session context.
|
|
178
|
+
const resolveExplicit = options?.explicitPdResolver ?? resolveExplicitPdSources;
|
|
179
|
+
const explicit = resolveExplicit();
|
|
180
|
+
if (explicit) {
|
|
181
|
+
let consistencyWarning;
|
|
182
|
+
if (ctx.workspaceDir) {
|
|
183
|
+
const normalizedCtx = path.resolve(ctx.workspaceDir);
|
|
184
|
+
const normalizedExplicit = path.resolve(explicit.workspaceDir);
|
|
185
|
+
if (normalizedCtx !== normalizedExplicit) {
|
|
186
|
+
consistencyWarning =
|
|
187
|
+
`PD explicit workspace (${explicit.source}: ${explicit.workspaceDir}) ` +
|
|
188
|
+
`differs from OpenClaw context (${ctx.workspaceDir}). Using PD explicit.`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
ok: true,
|
|
193
|
+
workspaceDir: explicit.workspaceDir,
|
|
194
|
+
source: explicit.source,
|
|
195
|
+
consistencyWarning,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
// Priority 2: OpenClaw live context — the real session workspace.
|
|
199
|
+
// This MUST take priority over pd_default (the hardcoded fallback).
|
|
200
|
+
if (ctx.workspaceDir) {
|
|
201
|
+
const issue = validateWorkspaceDir(ctx.workspaceDir);
|
|
202
|
+
if (!issue) {
|
|
203
|
+
return {
|
|
204
|
+
ok: true,
|
|
205
|
+
workspaceDir: ctx.workspaceDir,
|
|
206
|
+
source: 'openclaw_context',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Priority 3: OpenClaw API resolution
|
|
211
|
+
const apiResolved = tryResolveFromOpenClawApi(api, ctx.agentId);
|
|
212
|
+
if (apiResolved) {
|
|
213
|
+
return {
|
|
214
|
+
ok: true,
|
|
215
|
+
workspaceDir: apiResolved,
|
|
216
|
+
source: 'openclaw_api',
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
// Priority 4: pd_default (hardcoded fallback) — only when nothing else works
|
|
220
|
+
const resolveCanonical = options?.canonicalResolver ?? resolveCanonicalWorkspaceDir;
|
|
221
|
+
const canonical = resolveCanonical();
|
|
222
|
+
if (canonical && canonical.source === 'pd_default') {
|
|
223
|
+
return {
|
|
224
|
+
ok: true,
|
|
225
|
+
workspaceDir: canonical.workspaceDir,
|
|
226
|
+
source: 'pd_default',
|
|
227
|
+
consistencyWarning: 'Using hardcoded default workspace (~/.openclaw/workspace). ' +
|
|
228
|
+
'Set PD_WORKSPACE_DIR or create ~/.openclaw/principles-disciple.json for stable resolution.',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
reason: 'workspace_dir_unresolvable',
|
|
234
|
+
nextAction: 'Set PD_WORKSPACE_DIR environment variable, create ~/.openclaw/principles-disciple.json ' +
|
|
235
|
+
'with a "workspace" field, or ensure OpenClaw provides workspaceDir in hook context.',
|
|
236
|
+
message: `[PD:${source}] Cannot resolve workspace directory from any source. ` +
|
|
237
|
+
`PD explicit config (PD_WORKSPACE_DIR, principles-disciple.json) ` +
|
|
238
|
+
`and OpenClaw fallback (ctx.workspaceDir, api.resolveAgentWorkspaceDir, ~/.openclaw/workspace) all failed.`,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
72
241
|
/**
|
|
73
242
|
* Resolve workspace directory for tool hook execution (safe version).
|
|
74
243
|
* Returns undefined instead of throwing if resolution fails.
|
|
244
|
+
*
|
|
245
|
+
* PRI-259: Uses PD canonical config as primary source, OpenClaw as fallback.
|
|
75
246
|
*/
|
|
76
|
-
export function resolveToolHookWorkspaceDirSafe(ctx, api, source) {
|
|
77
|
-
|
|
247
|
+
export function resolveToolHookWorkspaceDirSafe(ctx, api, source, options) {
|
|
248
|
+
const result = resolveHookWorkspaceDir(ctx, api, source, options);
|
|
249
|
+
if (!result.ok) {
|
|
250
|
+
api.logger.warn(result.message);
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
if (result.consistencyWarning) {
|
|
254
|
+
api.logger.warn(`[PD:${source}] ${result.consistencyWarning}`);
|
|
255
|
+
}
|
|
256
|
+
return result.workspaceDir;
|
|
78
257
|
}
|
|
79
258
|
export class WorkspaceResolutionError extends Error {
|
|
80
259
|
reason;
|