principles-disciple 1.72.0 → 1.74.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/INSTALL.md +1 -3
- package/openclaw.plugin.json +10 -5
- package/package.json +17 -19
- package/scripts/acceptance-test.mjs +16 -73
- package/scripts/sync-plugin.mjs +382 -77
- package/src/commands/archive-impl.ts +2 -1
- package/src/commands/capabilities.ts +2 -2
- package/src/commands/context.ts +2 -2
- package/src/commands/disable-impl.ts +2 -1
- package/src/commands/evolution-status.ts +16 -16
- package/src/commands/export.ts +12 -67
- package/src/commands/pain.ts +91 -1
- package/src/commands/principle-rollback.ts +2 -1
- package/src/commands/promote-impl.ts +7 -43
- package/src/commands/rollback-impl.ts +2 -1
- package/src/commands/rollback.ts +2 -1
- package/src/commands/samples.ts +2 -1
- package/src/commands/thinking-os.ts +2 -1
- package/src/config/errors.ts +18 -2
- package/src/constants/diagnostician.ts +2 -2
- package/src/constants/tools.ts +2 -1
- package/src/core/__tests__/focus-history.test.ts +210 -0
- package/src/core/config.ts +1 -1
- package/src/core/correction-cue-learner.ts +2 -136
- package/src/core/correction-types.ts +16 -88
- package/src/core/dictionary.ts +19 -20
- package/src/core/empathy-keyword-matcher.ts +17 -289
- package/src/core/empathy-types.ts +18 -229
- package/src/core/event-log.ts +29 -132
- package/src/core/evolution-reducer.ts +21 -2
- package/src/core/evolution-types.ts +76 -464
- package/src/core/file-store.ts +80 -0
- package/src/core/focus-history.ts +228 -955
- package/src/core/local-worker-routing.ts +34 -314
- package/src/core/merge-gate-audit.ts +0 -195
- package/src/core/migration.ts +0 -1
- package/src/core/pain-diagnostic-gate.ts +154 -0
- package/src/core/pain-signal.ts +21 -138
- package/src/core/pain.ts +15 -88
- package/src/core/path-resolver.ts +0 -1
- package/src/core/paths.ts +0 -1
- package/src/core/pd-task-reconciler.ts +26 -115
- package/src/core/pd-task-service.ts +9 -9
- package/src/core/pd-task-types.ts +23 -127
- package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
- package/src/core/principle-compiler/code-validator.ts +15 -42
- package/src/core/principle-compiler/compiler.ts +100 -15
- package/src/core/principle-compiler/index.ts +5 -2
- package/src/core/principle-compiler/template-generator.ts +4 -104
- package/src/core/principle-injection.ts +10 -202
- package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
- package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
- package/src/core/principle-tree-ledger-adapter.ts +145 -0
- package/src/core/principle-tree-ledger.ts +8 -6
- package/src/core/reflection/reflection-context.ts +14 -109
- package/src/core/replay-engine.ts +8 -500
- package/src/core/rule-host-helpers.ts +5 -35
- package/src/core/rule-host-types.ts +10 -82
- package/src/core/rule-host.ts +6 -63
- package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
- package/src/core/session-tracker.ts +87 -101
- package/src/core/shadow-observation-registry.ts +19 -48
- package/src/core/trajectory.ts +3 -1
- package/src/core/workflow-funnel-loader.ts +62 -68
- package/src/core/workspace-context.ts +46 -0
- package/src/core/workspace-dir-service.ts +1 -1
- package/src/core/workspace-dir-validation.ts +18 -9
- package/src/hooks/AGENTS.md +1 -1
- package/src/hooks/gate-block-helper.ts +71 -64
- package/src/hooks/gate.ts +183 -31
- package/src/hooks/lifecycle.ts +30 -32
- package/src/hooks/llm.ts +60 -32
- package/src/hooks/pain.ts +297 -103
- package/src/hooks/prompt.ts +400 -440
- package/src/hooks/subagent.ts +2 -29
- package/src/i18n/commands.ts +2 -10
- package/src/index.ts +95 -85
- package/src/openclaw-sdk.ts +311 -0
- package/src/service/central-database.ts +8 -4
- package/src/service/evolution-queue-migration.ts +2 -1
- package/src/service/evolution-worker.ts +163 -1786
- package/src/service/internalization-trigger-adapter.ts +302 -0
- package/src/service/keyword-optimization-service.ts +4 -4
- package/src/service/monitoring-query-service.ts +1 -215
- package/src/service/queue-io.ts +60 -331
- package/src/service/runtime-summary-service.ts +59 -16
- package/src/service/subagent-workflow/index.ts +0 -41
- package/src/service/subagent-workflow/types.ts +9 -120
- package/src/service/subagent-workflow/workflow-store.ts +2 -119
- package/src/service/workflow-watchdog.ts +0 -43
- package/src/types/event-payload.ts +16 -74
- package/src/types/event-types.ts +38 -547
- package/src/types/hygiene-types.ts +7 -30
- package/src/types/principle-tree-schema.ts +20 -222
- package/src/types/queue.ts +15 -70
- package/src/types/runtime-summary.ts +5 -49
- package/src/utils/io.ts +8 -20
- package/src/utils/retry.ts +1 -1
- package/src/utils/shadow-fingerprint.ts +2 -2
- package/src/utils/workspace-resolver.ts +50 -0
- package/templates/langs/en/core/AGENTS.md +7 -7
- package/templates/langs/en/core/BOOT.md +1 -1
- package/templates/langs/en/core/HEARTBEAT.md +2 -2
- package/templates/langs/en/principles/THINKING_OS.md +3 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/en/skills/evolve-task/SKILL.md +3 -3
- package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-mentor/SKILL.md +2 -3
- package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
- package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
- package/templates/langs/zh/core/AGENTS.md +7 -7
- package/templates/langs/zh/core/BOOT.md +1 -1
- package/templates/langs/zh/core/HEARTBEAT.md +2 -2
- package/templates/langs/zh/principles/THINKING_OS.md +3 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
- package/templates/langs/zh/skills/evolve-task/SKILL.md +4 -4
- package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-mentor/SKILL.md +2 -3
- package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
- package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
- package/tests/build-artifacts.test.ts +1 -3
- package/tests/commands/evolution-status.test.ts +0 -118
- package/tests/core/bootstrap-rules.test.ts +1 -1
- package/tests/core/config.test.ts +1 -1
- package/tests/core/event-log.test.ts +35 -0
- package/tests/core/evolution-engine.test.ts +610 -0
- package/tests/core/file-store.test.ts +102 -0
- package/tests/core/focus-history.test.ts +203 -11
- package/tests/core/merge-gate-audit.test.ts +2 -169
- package/tests/core/migration.test.ts +7 -7
- package/tests/core/model-deployment-registry.test.ts +7 -1
- package/tests/core/model-training-registry.test.ts +19 -0
- package/tests/core/observability.test.ts +0 -1
- package/tests/core/pain-diagnostic-gate.test.ts +498 -0
- package/tests/core/pain.test.ts +0 -1
- package/tests/core/path-resolver.test.ts +1 -1
- package/tests/core/paths-refactor.test.ts +0 -22
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
- package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
- package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
- package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
- package/tests/core/reflection-context.test.ts +0 -14
- package/tests/core/replay-engine.test.ts +127 -215
- package/tests/core/rule-host-helpers.test.ts +2 -2
- package/tests/core/rule-implementation-runtime.test.ts +0 -27
- package/tests/core/workflow-funnel-loader.test.ts +162 -0
- package/tests/core/workspace-context.test.ts +2 -2
- package/tests/core/workspace-dir-validation.test.ts +8 -1
- package/tests/core-anti-growth.test.ts +191 -0
- package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
- package/tests/hooks/confirm-first-removal.test.ts +188 -0
- package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
- package/tests/hooks/gate-auto-correct.test.ts +665 -0
- package/tests/hooks/gate-no-path-write-tool.test.ts +172 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
- package/tests/hooks/pain.test.ts +269 -12
- package/tests/hooks/prompt-characterization.test.ts +500 -0
- package/tests/hooks/prompt-size-guard.test.ts +32 -17
- package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
- package/tests/index.test.ts +94 -1
- package/tests/integration/auto-entry-gate.test.ts +248 -0
- package/tests/integration/internalization-trigger-guard.test.ts +69 -0
- package/tests/integration/m8-legacy-paths.test.ts +63 -0
- package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
- package/tests/plugin-config-resolution-cutover.test.ts +359 -0
- package/tests/runtime-v2-discovery-guard.test.ts +154 -0
- package/tests/service/central-database.test.ts +457 -0
- package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
- package/tests/service/evolution-worker.timeout.test.ts +11 -129
- package/tests/service/internalization-trigger-adapter.test.ts +251 -0
- package/tests/service/monitoring-query-service.test.ts +1 -47
- package/tests/service/queue-io.test.ts +1 -62
- package/tests/service/runtime-summary-service.test.ts +3 -1
- package/tests/service/workflow-watchdog.test.ts +0 -91
- package/tests/utils/file-lock.test.ts +5 -3
- package/tests/utils/session-key.test.ts +52 -0
- package/tests/utils/subagent-probe.test.ts +48 -1
- package/vitest.config.ts +4 -11
- package/.planning/codebase/ARCHITECTURE.md +0 -157
- package/.planning/codebase/CONCERNS.md +0 -145
- package/.planning/codebase/CONVENTIONS.md +0 -148
- package/.planning/codebase/INTEGRATIONS.md +0 -81
- package/.planning/codebase/STACK.md +0 -87
- package/.planning/codebase/STRUCTURE.md +0 -193
- package/.planning/codebase/TESTING.md +0 -243
- package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
- package/docs/COMMAND_REFERENCE.md +0 -76
- package/docs/COMMAND_REFERENCE_EN.md +0 -79
- package/scripts/build-web.mjs +0 -46
- package/scripts/diagnose-nocturnal.mjs +0 -537
- package/scripts/seed-nocturnal-scenarios.mjs +0 -384
- package/src/commands/nocturnal-review.ts +0 -322
- package/src/commands/nocturnal-rollout.ts +0 -790
- package/src/commands/nocturnal-train.ts +0 -986
- package/src/commands/pd-reflect.ts +0 -88
- package/src/core/adaptive-thresholds.ts +0 -478
- package/src/core/diagnostician-task-store.ts +0 -192
- package/src/core/nocturnal-arbiter.ts +0 -715
- package/src/core/nocturnal-artifact-lineage.ts +0 -116
- package/src/core/nocturnal-artificer.ts +0 -257
- package/src/core/nocturnal-candidate-scoring.ts +0 -530
- package/src/core/nocturnal-compliance.ts +0 -1146
- package/src/core/nocturnal-dataset.ts +0 -763
- package/src/core/nocturnal-executability.ts +0 -428
- package/src/core/nocturnal-export.ts +0 -499
- package/src/core/nocturnal-paths.ts +0 -240
- package/src/core/nocturnal-reasoning-deriver.ts +0 -343
- package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
- package/src/core/nocturnal-snapshot-contract.ts +0 -99
- package/src/core/nocturnal-trajectory-extractor.ts +0 -512
- package/src/core/nocturnal-trinity-types.ts +0 -218
- package/src/core/nocturnal-trinity.ts +0 -2680
- package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
- package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
- package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
- package/src/http/principles-console-route.ts +0 -709
- package/src/service/central-health-service.ts +0 -49
- package/src/service/central-overview-service.ts +0 -138
- package/src/service/control-ui-query-service.ts +0 -900
- package/src/service/cooldown-strategy.ts +0 -97
- package/src/service/evolution-pain-context.ts +0 -79
- package/src/service/evolution-query-service.ts +0 -407
- package/src/service/health-query-service.ts +0 -1038
- package/src/service/nocturnal-config.ts +0 -214
- package/src/service/nocturnal-runtime.ts +0 -734
- package/src/service/nocturnal-service.ts +0 -1605
- package/src/service/nocturnal-target-selector.ts +0 -545
- package/src/service/sleep-cycle.ts +0 -157
- package/src/service/startup-reconciler.ts +0 -112
- package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
- package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
- package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
- package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
- package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
- package/src/tools/write-pain-flag.ts +0 -215
- package/templates/langs/en/skills/plan-script/SKILL.md +0 -32
- package/templates/langs/zh/skills/plan-script/SKILL.md +0 -32
- package/tests/commands/nocturnal-review.test.ts +0 -448
- package/tests/commands/nocturnal-train.test.ts +0 -97
- package/tests/commands/pd-reflect.test.ts +0 -49
- package/tests/core/adaptive-thresholds.test.ts +0 -261
- package/tests/core/nocturnal-arbiter.test.ts +0 -559
- package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
- package/tests/core/nocturnal-artificer.test.ts +0 -241
- package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
- package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
- package/tests/core/nocturnal-compliance.test.ts +0 -646
- package/tests/core/nocturnal-dataset.test.ts +0 -892
- package/tests/core/nocturnal-e2e.test.ts +0 -234
- package/tests/core/nocturnal-executability.test.ts +0 -357
- package/tests/core/nocturnal-export.test.ts +0 -517
- package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
- package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
- package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
- package/tests/core/nocturnal-trinity.test.ts +0 -2053
- package/tests/core/pain-auto-repair.test.ts +0 -96
- package/tests/core/pain-integration.test.ts +0 -510
- package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
- package/tests/http/principles-console-route.test.ts +0 -162
- package/tests/integration/chaos-resilience.test.ts +0 -348
- package/tests/integration/empathy-workflow-integration.test.ts +0 -626
- package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
- package/tests/service/control-ui-query-service.test.ts +0 -121
- package/tests/service/cooldown-strategy.test.ts +0 -164
- package/tests/service/data-endpoints-regression.test.ts +0 -834
- package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
- package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
- package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
- package/tests/service/nocturnal-runtime.test.ts +0 -473
- package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
- package/tests/service/nocturnal-target-selector.test.ts +0 -615
- package/tests/service/startup-reconciler.test.ts +0 -148
- package/tests/tools/write-pain-flag.test.ts +0 -358
- package/ui/src/App.tsx +0 -45
- package/ui/src/api.ts +0 -220
- package/ui/src/charts.tsx +0 -955
- package/ui/src/components/ErrorState.tsx +0 -6
- package/ui/src/components/Loading.tsx +0 -13
- package/ui/src/components/ProtectedRoute.tsx +0 -12
- package/ui/src/components/Shell.tsx +0 -91
- package/ui/src/components/WorkspaceConfig.tsx +0 -178
- package/ui/src/components/index.ts +0 -5
- package/ui/src/context/auth.tsx +0 -80
- package/ui/src/context/theme.tsx +0 -66
- package/ui/src/hooks/useAutoRefresh.ts +0 -39
- package/ui/src/i18n/ui.ts +0 -473
- package/ui/src/main.tsx +0 -16
- package/ui/src/pages/EvolutionPage.tsx +0 -333
- package/ui/src/pages/FeedbackPage.tsx +0 -138
- package/ui/src/pages/GateMonitorPage.tsx +0 -136
- package/ui/src/pages/LoginPage.tsx +0 -89
- package/ui/src/pages/OverviewPage.tsx +0 -599
- package/ui/src/pages/SamplesPage.tsx +0 -174
- package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
- package/ui/src/styles.css +0 -2020
- package/ui/src/types.ts +0 -384
- package/ui/src/utils/format.ts +0 -15
|
@@ -30,11 +30,10 @@
|
|
|
30
30
|
* - Observations are retained until cleanup removes expired entries
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
import * as fs from 'fs';
|
|
34
33
|
import * as path from 'path';
|
|
35
34
|
import * as crypto from 'crypto';
|
|
36
35
|
import { withLock } from '../utils/file-lock.js';
|
|
37
|
-
import {
|
|
36
|
+
import { JsonFileStore } from './file-store.js';
|
|
38
37
|
|
|
39
38
|
// ---------------------------------------------------------------------------
|
|
40
39
|
// Constants
|
|
@@ -170,67 +169,44 @@ export interface ShadowRegistry {
|
|
|
170
169
|
}
|
|
171
170
|
|
|
172
171
|
// ---------------------------------------------------------------------------
|
|
173
|
-
// Registry Path
|
|
172
|
+
// Registry Path & Store
|
|
174
173
|
// ---------------------------------------------------------------------------
|
|
175
174
|
|
|
175
|
+
let _store: Map<string, JsonFileStore<ShadowRegistry>> = new Map();
|
|
176
|
+
|
|
176
177
|
function getRegistryPath(stateDir: string): string {
|
|
177
178
|
return path.join(stateDir, SHADOW_REGISTRY_FILE);
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (!fs.existsSync(dir)) {
|
|
187
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
181
|
+
function getStore(stateDir: string): JsonFileStore<ShadowRegistry> {
|
|
182
|
+
let store = _store.get(stateDir);
|
|
183
|
+
if (!store) {
|
|
184
|
+
const filePath = path.join(stateDir, SHADOW_REGISTRY_FILE);
|
|
185
|
+
store = new JsonFileStore<ShadowRegistry>(filePath, () => ({ observations: [], version: 1 }));
|
|
186
|
+
_store.set(stateDir, store);
|
|
188
187
|
}
|
|
188
|
+
return store;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
// ---------------------------------------------------------------------------
|
|
192
192
|
// File Operations
|
|
193
193
|
// ---------------------------------------------------------------------------
|
|
194
194
|
|
|
195
|
-
/**
|
|
196
|
-
* Read the registry from disk. Returns empty registry if missing.
|
|
197
|
-
*/
|
|
198
|
-
function readRegistry(stateDir: string): ShadowRegistry {
|
|
199
|
-
const registryPath = getRegistryPath(stateDir);
|
|
200
|
-
if (!fs.existsSync(registryPath)) {
|
|
201
|
-
return { observations: [], version: 1 };
|
|
202
|
-
}
|
|
203
|
-
try {
|
|
204
|
-
const content = fs.readFileSync(registryPath, 'utf-8');
|
|
205
|
-
return JSON.parse(content) as ShadowRegistry;
|
|
206
|
-
} catch (err) {
|
|
207
|
-
console.warn(`[shadow-observation-registry] Registry corrupted at ${registryPath}, recovering with empty state: ${String(err)}`);
|
|
208
|
-
return { observations: [], version: 1 };
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Write the registry to disk atomically.
|
|
214
|
-
*/
|
|
215
|
-
function writeRegistry(stateDir: string, registry: ShadowRegistry): void {
|
|
216
|
-
ensureRegistryDir(stateDir);
|
|
217
|
-
const registryPath = getRegistryPath(stateDir);
|
|
218
|
-
atomicWriteFileSync(registryPath, JSON.stringify(registry, null, 2));
|
|
219
|
-
}
|
|
220
|
-
|
|
221
195
|
/**
|
|
222
196
|
* Execute a read-modify-write under an exclusive file lock.
|
|
223
197
|
*/
|
|
224
|
-
|
|
225
198
|
function withShadowRegistryLock<T>(
|
|
226
199
|
stateDir: string,
|
|
227
200
|
fn: (_registry: ShadowRegistry) => T
|
|
228
201
|
): T {
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
202
|
+
const store = getStore(stateDir);
|
|
203
|
+
const filePath = getRegistryPath(stateDir);
|
|
204
|
+
// Re-create lock behavior using the store's mutate under a simple fs-based lock
|
|
205
|
+
return withLock(filePath, () => {
|
|
206
|
+
const registry = store.load();
|
|
207
|
+
const result = fn(registry);
|
|
208
|
+
store.save(registry);
|
|
209
|
+
return result;
|
|
234
210
|
});
|
|
235
211
|
}
|
|
236
212
|
|
|
@@ -276,7 +252,6 @@ export function recordShadowRouting(
|
|
|
276
252
|
|
|
277
253
|
return withShadowRegistryLock(stateDir, (registry) => {
|
|
278
254
|
registry.observations.push(observation);
|
|
279
|
-
writeRegistry(stateDir, registry);
|
|
280
255
|
return observation;
|
|
281
256
|
});
|
|
282
257
|
}
|
|
@@ -326,7 +301,6 @@ export function completeShadowObservation(
|
|
|
326
301
|
extra: {},
|
|
327
302
|
};
|
|
328
303
|
|
|
329
|
-
writeRegistry(stateDir, registry);
|
|
330
304
|
return observation;
|
|
331
305
|
});
|
|
332
306
|
}
|
|
@@ -369,7 +343,6 @@ export function completeShadowObservationByTask(
|
|
|
369
343
|
extra: {},
|
|
370
344
|
};
|
|
371
345
|
|
|
372
|
-
writeRegistry(stateDir, registry);
|
|
373
346
|
return observation;
|
|
374
347
|
});
|
|
375
348
|
}
|
|
@@ -506,7 +479,6 @@ export function markObservationsUsedInGate(
|
|
|
506
479
|
obs.usedInGate = true;
|
|
507
480
|
}
|
|
508
481
|
}
|
|
509
|
-
writeRegistry(stateDir, registry);
|
|
510
482
|
});
|
|
511
483
|
}
|
|
512
484
|
|
|
@@ -530,7 +502,6 @@ export function cleanupExpiredObservations(
|
|
|
530
502
|
(o) => o.routedAt >= cutoff
|
|
531
503
|
);
|
|
532
504
|
removed = before - registry.observations.length;
|
|
533
|
-
writeRegistry(stateDir, registry);
|
|
534
505
|
});
|
|
535
506
|
|
|
536
507
|
return removed;
|
package/src/core/trajectory.ts
CHANGED
|
@@ -861,12 +861,13 @@ export class TrajectoryDatabase {
|
|
|
861
861
|
listUserTurnsForSession(sessionId: string): {
|
|
862
862
|
id: number;
|
|
863
863
|
turnIndex: number;
|
|
864
|
+
rawExcerpt: string;
|
|
864
865
|
correctionDetected: boolean;
|
|
865
866
|
correctionCue: string | null;
|
|
866
867
|
createdAt: string;
|
|
867
868
|
}[] {
|
|
868
869
|
const rows = this.db.prepare(`
|
|
869
|
-
SELECT id, turn_index, correction_detected, correction_cue, created_at
|
|
870
|
+
SELECT id, turn_index, raw_excerpt, correction_detected, correction_cue, created_at
|
|
870
871
|
FROM user_turns
|
|
871
872
|
WHERE session_id = ?
|
|
872
873
|
ORDER BY turn_index ASC
|
|
@@ -875,6 +876,7 @@ export class TrajectoryDatabase {
|
|
|
875
876
|
return rows.map((row) => ({
|
|
876
877
|
id: Number(row.id),
|
|
877
878
|
turnIndex: Number(row.turn_index),
|
|
879
|
+
rawExcerpt: String(row.raw_excerpt ?? ''),
|
|
878
880
|
correctionDetected: Boolean(row.correction_detected),
|
|
879
881
|
correctionCue: row.correction_cue ? String(row.correction_cue) : null,
|
|
880
882
|
createdAt: String(row.created_at),
|
|
@@ -18,16 +18,34 @@ import yaml from 'js-yaml';
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* A single stage in a workflow funnel.
|
|
21
|
+
* Policy fields are optional — existing stages without policy fields remain valid.
|
|
21
22
|
*/
|
|
22
23
|
export interface WorkflowStage {
|
|
23
|
-
/** Stage name within the funnel (e.g., 'dreamer_completed') */
|
|
24
24
|
name: string;
|
|
25
|
-
/** Event type string (e.g., 'nocturnal_dreamer_completed') */
|
|
26
25
|
eventType: string;
|
|
27
|
-
/** Event category (e.g., 'completed', 'created', 'blocked') */
|
|
28
26
|
eventCategory: string;
|
|
29
|
-
/** Dot-path to stats field (e.g., 'evolution.nocturnalDreamerCompleted') */
|
|
30
27
|
statsField: string;
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
successCriteria?: string;
|
|
30
|
+
legacyDisabled?: boolean;
|
|
31
|
+
observability?: {
|
|
32
|
+
enabled?: boolean;
|
|
33
|
+
emitEvents?: string[];
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Funnel-level policy that applies to all stages unless overridden per-stage.
|
|
39
|
+
*/
|
|
40
|
+
export interface FunnelPolicy {
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
stageOrder?: 'strict' | 'relaxed';
|
|
43
|
+
legacyDisabled?: boolean;
|
|
44
|
+
observability?: {
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
emitEvents?: string[];
|
|
47
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
48
|
+
};
|
|
31
49
|
}
|
|
32
50
|
|
|
33
51
|
/**
|
|
@@ -36,6 +54,7 @@ export interface WorkflowStage {
|
|
|
36
54
|
export interface WorkflowFunnel {
|
|
37
55
|
workflowId: string;
|
|
38
56
|
stages: WorkflowStage[];
|
|
57
|
+
policy?: FunnelPolicy;
|
|
39
58
|
}
|
|
40
59
|
|
|
41
60
|
/**
|
|
@@ -50,56 +69,31 @@ export interface WorkflowFunnelConfig {
|
|
|
50
69
|
// WorkflowFunnelLoader
|
|
51
70
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
52
71
|
|
|
53
|
-
/**
|
|
54
|
-
* Loads and watches workflows.yaml, building an in-memory WORKFLOW_FUNNELS table.
|
|
55
|
-
*
|
|
56
|
-
* Failure semantics (per Codex review):
|
|
57
|
-
* - Missing file: clears in-memory funnels, uses empty Map
|
|
58
|
-
* - Malformed YAML: preserves last known-good config, logs warning
|
|
59
|
-
* - Schema-invalid YAML: same as malformed YAML
|
|
60
|
-
*
|
|
61
|
-
* Usage:
|
|
62
|
-
* const loader = new WorkflowFunnelLoader(stateDir);
|
|
63
|
-
* const funnels = loader.getAllFunnels(); // Map<string, WorkflowStage[]>
|
|
64
|
-
* loader.watch(); // Enable hot reload
|
|
65
|
-
*/
|
|
66
72
|
export class WorkflowFunnelLoader {
|
|
67
|
-
/** In-memory WORKFLOW_FUNNELS table: workflowId -> stages */
|
|
68
73
|
private readonly funnels = new Map<string, WorkflowStage[]>();
|
|
69
|
-
|
|
74
|
+
private readonly fullFunnels = new Map<string, WorkflowFunnel>();
|
|
70
75
|
private readonly configPath: string;
|
|
71
|
-
|
|
72
|
-
/** fs.watch() handle for cleanup */
|
|
73
76
|
private watchHandle?: fs.FSWatcher;
|
|
74
|
-
|
|
75
|
-
/** YAML parse warnings from last load() call */
|
|
76
77
|
private readonly warnings: string[] = [];
|
|
77
78
|
|
|
78
79
|
constructor(stateDir: string) {
|
|
79
|
-
// D-02: workflows.yaml in .state/ directory
|
|
80
80
|
this.configPath = path.join(stateDir, 'workflows.yaml');
|
|
81
81
|
this.load();
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
/**
|
|
85
|
-
* Load (or reload) workflows.yaml from disk.
|
|
86
|
-
* On parse/validation failure, preserves the last known-good config.
|
|
87
|
-
* On missing file, clears to empty.
|
|
88
|
-
*/
|
|
89
84
|
load(): void {
|
|
90
|
-
this.warnings.length = 0;
|
|
85
|
+
this.warnings.length = 0;
|
|
91
86
|
if (!fs.existsSync(this.configPath)) {
|
|
92
87
|
this.warnings.push('workflows.yaml file not found.');
|
|
93
88
|
this.funnels.clear();
|
|
89
|
+
this.fullFunnels.clear();
|
|
94
90
|
return;
|
|
95
91
|
}
|
|
96
92
|
|
|
97
93
|
try {
|
|
98
94
|
const content = fs.readFileSync(this.configPath, 'utf-8');
|
|
99
|
-
// Use safe load — no arbitrary code execution
|
|
100
95
|
const config = yaml.load(content, { schema: yaml.DEFAULT_SCHEMA }) as WorkflowFunnelConfig;
|
|
101
96
|
|
|
102
|
-
// Validate top-level structure
|
|
103
97
|
if (!config || typeof config.version !== 'string' || !Array.isArray(config.funnels)) {
|
|
104
98
|
const msg = 'workflows.yaml validation failed: missing version or funnels array. Preserving last valid config.';
|
|
105
99
|
console.warn(`[WorkflowFunnelLoader] ${msg}`);
|
|
@@ -107,11 +101,12 @@ export class WorkflowFunnelLoader {
|
|
|
107
101
|
return;
|
|
108
102
|
}
|
|
109
103
|
|
|
110
|
-
// Rebuild funnels map
|
|
111
104
|
const newFunnels = new Map<string, WorkflowStage[]>();
|
|
105
|
+
const newFullFunnels = new Map<string, WorkflowFunnel>();
|
|
112
106
|
for (const funnel of config.funnels) {
|
|
113
107
|
if (funnel?.workflowId && typeof funnel.workflowId === 'string' && Array.isArray(funnel.stages)) {
|
|
114
108
|
newFunnels.set(funnel.workflowId, funnel.stages);
|
|
109
|
+
newFullFunnels.set(funnel.workflowId, funnel);
|
|
115
110
|
} else {
|
|
116
111
|
const msg = 'Skipping invalid funnel entry: missing workflowId or stages.';
|
|
117
112
|
console.warn(`[WorkflowFunnelLoader] ${msg}`);
|
|
@@ -119,44 +114,28 @@ export class WorkflowFunnelLoader {
|
|
|
119
114
|
}
|
|
120
115
|
}
|
|
121
116
|
|
|
122
|
-
// Atomic replace: only commit if entire parse/validation succeeded
|
|
123
117
|
this.funnels.clear();
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
118
|
+
this.fullFunnels.clear();
|
|
119
|
+
for (const [k, v] of newFunnels) { this.funnels.set(k, v); }
|
|
120
|
+
for (const [k, v] of newFullFunnels) { this.fullFunnels.set(k, v); }
|
|
127
121
|
} catch (err) {
|
|
128
|
-
// Best-effort: preserve last known-good config on parse error
|
|
129
122
|
const msg = `Failed to parse workflows.yaml: ${String(err)}. Preserving last valid config.`;
|
|
130
123
|
console.warn(`[WorkflowFunnelLoader] ${msg}`);
|
|
131
124
|
this.warnings.push(msg);
|
|
132
125
|
}
|
|
133
126
|
}
|
|
134
127
|
|
|
135
|
-
/**
|
|
136
|
-
* Start watching workflows.yaml for changes.
|
|
137
|
-
* Calls load() automatically when the file changes.
|
|
138
|
-
* No-op if the config file does not exist.
|
|
139
|
-
*/
|
|
140
128
|
watch(): void {
|
|
141
|
-
// WATCHER-01: re-entry guard — prevent FSWatcher leak on double-watch
|
|
142
129
|
if (this.watchHandle) return;
|
|
143
|
-
// Guard: fs.watch fails with ENOENT if the path does not exist
|
|
144
130
|
if (!fs.existsSync(this.configPath)) return;
|
|
145
|
-
// Debounce: only re-read after file write settles (100ms)
|
|
146
131
|
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
147
132
|
this.watchHandle = fs.watch(this.configPath, (eventType) => {
|
|
148
|
-
// PLAT-01: handle both 'change' and 'rename' events for Windows compatibility
|
|
149
133
|
if (eventType !== 'change' && eventType !== 'rename') return;
|
|
150
134
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
151
|
-
debounceTimer = setTimeout(() =>
|
|
152
|
-
this.load();
|
|
153
|
-
}, 100);
|
|
135
|
+
debounceTimer = setTimeout(() => this.load(), 100);
|
|
154
136
|
});
|
|
155
137
|
}
|
|
156
138
|
|
|
157
|
-
/**
|
|
158
|
-
* Stop watching and clean up the FSWatcher.
|
|
159
|
-
*/
|
|
160
139
|
dispose(): void {
|
|
161
140
|
if (this.watchHandle) {
|
|
162
141
|
this.watchHandle.close();
|
|
@@ -164,38 +143,53 @@ export class WorkflowFunnelLoader {
|
|
|
164
143
|
}
|
|
165
144
|
}
|
|
166
145
|
|
|
167
|
-
/**
|
|
168
|
-
* Get all stages for a workflow.
|
|
169
|
-
*/
|
|
170
146
|
getStages(workflowId: string): WorkflowStage[] {
|
|
171
147
|
return this.funnels.get(workflowId) ?? [];
|
|
172
148
|
}
|
|
173
149
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
150
|
+
getFunnel(workflowId: string): WorkflowFunnel | undefined {
|
|
151
|
+
const funnel = this.fullFunnels.get(workflowId);
|
|
152
|
+
if (!funnel) return undefined;
|
|
153
|
+
return this.cloneFunnel(funnel);
|
|
154
|
+
}
|
|
155
|
+
|
|
178
156
|
getAllFunnels(): Map<string, WorkflowStage[]> {
|
|
179
157
|
const result = new Map<string, WorkflowStage[]>();
|
|
180
158
|
for (const [k, v] of this.funnels) {
|
|
181
|
-
// WATCHER-03: deep-clone arrays and stage objects
|
|
182
159
|
result.set(k, v.map(stage => ({ ...stage })));
|
|
183
160
|
}
|
|
184
161
|
return result;
|
|
185
162
|
}
|
|
186
163
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
164
|
+
getAllFunnelsWithPolicy(): Map<string, WorkflowFunnel> {
|
|
165
|
+
const result = new Map<string, WorkflowFunnel>();
|
|
166
|
+
for (const [k, v] of this.fullFunnels) {
|
|
167
|
+
result.set(k, this.cloneFunnel(v));
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
191
172
|
getWarnings(): string[] {
|
|
192
173
|
return [...this.warnings];
|
|
193
174
|
}
|
|
194
175
|
|
|
195
|
-
/**
|
|
196
|
-
* Get the config file path (for testing/debugging).
|
|
197
|
-
*/
|
|
198
176
|
getConfigPath(): string {
|
|
199
177
|
return this.configPath;
|
|
200
178
|
}
|
|
179
|
+
|
|
180
|
+
private cloneFunnel(funnel: WorkflowFunnel): WorkflowFunnel {
|
|
181
|
+
return {
|
|
182
|
+
...funnel,
|
|
183
|
+
stages: funnel.stages.map(stage => ({ ...stage })),
|
|
184
|
+
policy: funnel.policy ? {
|
|
185
|
+
...funnel.policy,
|
|
186
|
+
observability: funnel.policy.observability ? {
|
|
187
|
+
...funnel.policy.observability,
|
|
188
|
+
emitEvents: funnel.policy.observability.emitEvents
|
|
189
|
+
? [...funnel.policy.observability.emitEvents]
|
|
190
|
+
: undefined,
|
|
191
|
+
} : undefined,
|
|
192
|
+
} : undefined,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
201
195
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PD_FILES } from './paths.js';
|
|
2
2
|
import { resolvePdPath } from './paths.js';
|
|
3
3
|
import { PathResolver } from './path-resolver.js';
|
|
4
|
+
import { validateWorkspaceDir } from './workspace-dir-validation.js';
|
|
4
5
|
import { ConfigService } from './config-service.js';
|
|
5
6
|
import type { PainConfig } from './config.js';
|
|
6
7
|
import type { EventLog } from './event-log.js';
|
|
@@ -193,6 +194,14 @@ export class WorkspaceContext {
|
|
|
193
194
|
}
|
|
194
195
|
}
|
|
195
196
|
|
|
197
|
+
const validationIssue = validateWorkspaceDir(workspaceDir);
|
|
198
|
+
if (validationIssue !== null) {
|
|
199
|
+
logWarn(
|
|
200
|
+
`[PD:WorkspaceContext] LEGACY_PATH_RESOLVER_FALLBACK: ${validationIssue}. ` +
|
|
201
|
+
'This is a legacy discovery path; explicit workspaceDir should be provided in the hook context.',
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
196
205
|
const existing = this.instances.get(workspaceDir);
|
|
197
206
|
if (existing) return existing;
|
|
198
207
|
|
|
@@ -210,6 +219,43 @@ export class WorkspaceContext {
|
|
|
210
219
|
return instance;
|
|
211
220
|
}
|
|
212
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Creates a WorkspaceContext requiring explicit workspaceDir.
|
|
224
|
+
* For Runtime V2 entrypoints where implicit PathResolver fallback is unacceptable.
|
|
225
|
+
* @throws Error if workspaceDir is not provided in the context.
|
|
226
|
+
*/
|
|
227
|
+
static fromHookContextExplicit(ctx: { workspaceDir?: string; logger?: { error?: (...args: unknown[]) => void; warn?: (...args: unknown[]) => void; info?: (...args: unknown[]) => void } }): WorkspaceContext {
|
|
228
|
+
const { logger } = ctx;
|
|
229
|
+
let { workspaceDir } = ctx;
|
|
230
|
+
if (!workspaceDir || !workspaceDir.trim()) {
|
|
231
|
+
const error = {
|
|
232
|
+
ok: false as const,
|
|
233
|
+
reason: 'workspace_dir_missing',
|
|
234
|
+
message: 'workspaceDir is required for Runtime V2 entrypoints. Provide it explicitly in the hook context.',
|
|
235
|
+
nextAction: 'Ensure the OpenClaw hook context includes workspaceDir, or use PD_WORKSPACE_DIR env var.',
|
|
236
|
+
};
|
|
237
|
+
logger?.error?.(`[PD:WorkspaceContext] ${error.message}`);
|
|
238
|
+
throw new Error(`[PD:WorkspaceContext] ${error.reason}: ${error.message}`);
|
|
239
|
+
}
|
|
240
|
+
const normalized = this.pathResolver.normalizeWorkspacePath(workspaceDir);
|
|
241
|
+
if (normalized !== workspaceDir) {
|
|
242
|
+
logger?.info?.(`[PD:WorkspaceContext] Normalized workspaceDir before validation: ${workspaceDir} -> ${normalized}`);
|
|
243
|
+
workspaceDir = normalized;
|
|
244
|
+
}
|
|
245
|
+
const validationIssue = validateWorkspaceDir(workspaceDir);
|
|
246
|
+
if (validationIssue !== null) {
|
|
247
|
+
const error = {
|
|
248
|
+
ok: false as const,
|
|
249
|
+
reason: 'workspace_dir_invalid',
|
|
250
|
+
message: `workspaceDir validation failed for Runtime V2 entrypoint: ${validationIssue}`,
|
|
251
|
+
nextAction: 'Provide a valid workspaceDir that is not the home directory, root, or empty.',
|
|
252
|
+
};
|
|
253
|
+
logger?.error?.(`[PD:WorkspaceContext] ${error.message}`);
|
|
254
|
+
throw new Error(`[PD:WorkspaceContext] ${error.reason}: ${error.message}`);
|
|
255
|
+
}
|
|
256
|
+
return this.fromHookContext({ ...ctx, workspaceDir });
|
|
257
|
+
}
|
|
258
|
+
|
|
213
259
|
/**
|
|
214
260
|
* Resolves a PD file path within the workspace.
|
|
215
261
|
*/
|
|
@@ -19,7 +19,7 @@ function tryResolveFromAgent(
|
|
|
19
19
|
attempts: string[],
|
|
20
20
|
): string | undefined {
|
|
21
21
|
try {
|
|
22
|
-
const resolved = api.runtime
|
|
22
|
+
const resolved = api.runtime?.agent?.resolveAgentWorkspaceDir?.(api.config, agentId);
|
|
23
23
|
const issue = validateWorkspaceDir(resolved);
|
|
24
24
|
if (!issue) {
|
|
25
25
|
return resolved;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
2
|
* WorkspaceDir Validation Utilities
|
|
3
3
|
*
|
|
4
4
|
* This module only validates candidate workspace directories and delegates
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import * as os from 'os';
|
|
9
|
+
import * as path from 'path';
|
|
9
10
|
|
|
10
11
|
export interface WorkspaceResolutionContext {
|
|
11
12
|
workspaceDir?: string;
|
|
@@ -17,27 +18,35 @@ export function validateWorkspaceDir(dir: string | undefined): string | null {
|
|
|
17
18
|
return 'workspaceDir is undefined/null';
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
if (/^[A-Za-z]:\\?$/.test(dir)) {
|
|
22
|
+
return `workspaceDir is a drive root: "${dir}"`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const resolved = path.resolve(dir);
|
|
20
26
|
const homeDir = os.homedir();
|
|
21
27
|
|
|
22
|
-
if (
|
|
28
|
+
if (resolved === homeDir) {
|
|
23
29
|
return `workspaceDir equals home directory (${homeDir}), likely missing context field`;
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
if (
|
|
27
|
-
return `workspaceDir is root or empty: "${
|
|
32
|
+
if (resolved === '/' || resolved === '') {
|
|
33
|
+
return `workspaceDir is root or empty: "${resolved}"`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (/^[A-Za-z]:\\?$/.test(resolved)) {
|
|
37
|
+
return `workspaceDir is a drive root: "${resolved}"`;
|
|
28
38
|
}
|
|
29
39
|
|
|
30
40
|
const escapedHome = homeDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
31
41
|
const badPatterns = [
|
|
32
|
-
{ pattern: new RegExp(`^${escapedHome}
|
|
33
|
-
{ pattern: new RegExp(`^${escapedHome}/$`), desc: 'is home directory with trailing slash' },
|
|
42
|
+
{ pattern: new RegExp(`^${escapedHome}[\\\\/]?$`), desc: 'is home directory' },
|
|
34
43
|
];
|
|
35
44
|
|
|
36
45
|
for (const { pattern, desc } of badPatterns) {
|
|
37
|
-
if (pattern.test(
|
|
38
|
-
return `workspaceDir ${desc}: "${
|
|
46
|
+
if (pattern.test(resolved)) {
|
|
47
|
+
return `workspaceDir ${desc}: "${resolved}"`;
|
|
39
48
|
}
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
return null;
|
|
43
|
-
}
|
|
52
|
+
}
|
package/src/hooks/AGENTS.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|------------|------|--------------|
|
|
9
9
|
| `before_prompt_build` | `prompt.ts` | Multi-layer context injection: identity, trust, evolution, principles, thinking OS |
|
|
10
10
|
| `before_tool_call` | `gate.ts` | Security gate: trust stage checks, risk path blocking, bash security (Cyrillic de-obfuscation, command tokenization) |
|
|
11
|
-
| `after_tool_call` | `pain.ts` | Pain detection: failure → pain score →
|
|
11
|
+
| `after_tool_call` | `pain.ts` | Pain detection: failure → pain score → Runtime V2 `PainSignalBridge` |
|
|
12
12
|
| `before_compaction` | `lifecycle.ts` | Checkpoints state before context loss |
|
|
13
13
|
| `after_compaction` | `lifecycle.ts` | State recovery |
|
|
14
14
|
| `before_reset` / `session_*` | `lifecycle.ts` | Session lifecycle management |
|