principles-disciple 1.34.1 → 1.35.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/.dependency-cruiser.json +19 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +6 -3
- package/src/config/defaults/runtime.ts +100 -24
- package/src/core/event-log.ts +87 -20
- package/src/core/nocturnal-candidate-scoring.ts +6 -6
- package/src/core/nocturnal-trinity-types.ts +94 -0
- package/src/core/nocturnal-trinity.ts +35 -99
- package/src/core/session-tracker.ts +7 -6
- package/src/core/system-logger.ts +104 -12
- package/src/core/workspace-dir-service.ts +40 -6
- package/src/core/workspace-dir-validation.ts +5 -37
- package/src/hooks/trajectory-collector.ts +7 -7
- package/src/index.ts +8 -68
- package/src/service/central-sync-service.ts +3 -8
- package/src/service/correction-observer-workflow-manager.ts +2 -2
- package/src/service/evolution-worker.ts +30 -35
- package/src/service/nocturnal-service.ts +72 -47
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +4 -4
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +4 -4
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +2 -2
- package/src/service/subagent-workflow/types.ts +69 -3
- package/src/utils/shadow-fingerprint.ts +42 -0
- package/src/utils/workspace-resolver.ts +54 -0
- package/tests/core/workspace-dir-validation.test.ts +1 -1
- package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +3 -3
- package/vitest.config.ts +53 -6
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Directory Resolution Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared helpers for resolving workspace directories across commands and hooks.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
8
|
+
import { validateWorkspaceDir, type WorkspaceResolutionContext } from '../core/workspace-dir-validation.js';
|
|
9
|
+
import { resolveWorkspaceDir } from '../core/workspace-dir-service.js';
|
|
10
|
+
import { resolveWorkspaceDirFromApi } from '../core/path-resolver.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolve workspace directory for command execution.
|
|
14
|
+
*
|
|
15
|
+
* Chain: ctx.workspaceDir → resolveWorkspaceDirFromApi (official OpenClaw API + env vars)
|
|
16
|
+
*
|
|
17
|
+
* CRITICAL: Throws if workspaceDir cannot be resolved. Silent failures are dangerous
|
|
18
|
+
* because commands might operate on the wrong directory.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveCommandWorkspaceDir(
|
|
21
|
+
api: OpenClawPluginApi,
|
|
22
|
+
ctx: { workspaceDir?: string },
|
|
23
|
+
): string {
|
|
24
|
+
// 1. Direct from command context (most reliable — set by OpenClaw for current session)
|
|
25
|
+
if (ctx.workspaceDir) {
|
|
26
|
+
const issue = validateWorkspaceDir(ctx.workspaceDir);
|
|
27
|
+
if (!issue) return ctx.workspaceDir;
|
|
28
|
+
api.logger.error(`[PD:Command] ctx.workspaceDir="${ctx.workspaceDir}" is invalid: ${issue}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 2. Official OpenClaw API → env vars → config file
|
|
32
|
+
const resolved = resolveWorkspaceDirFromApi(api);
|
|
33
|
+
if (resolved) return resolved;
|
|
34
|
+
|
|
35
|
+
// CRITICAL FAILURE: Cannot determine workspace directory
|
|
36
|
+
const errorMsg = `[PD:Command] CRITICAL: Cannot resolve workspace directory. ` +
|
|
37
|
+
`ctx.workspaceDir="${ctx.workspaceDir}" is invalid, and all fallbacks failed. ` +
|
|
38
|
+
`Commands will NOT execute to prevent data corruption.`;
|
|
39
|
+
api.logger.error(errorMsg);
|
|
40
|
+
|
|
41
|
+
throw new Error(errorMsg);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve workspace directory for tool hook execution (safe version).
|
|
46
|
+
* Returns undefined instead of throwing if resolution fails.
|
|
47
|
+
*/
|
|
48
|
+
export function resolveToolHookWorkspaceDirSafe(
|
|
49
|
+
ctx: WorkspaceResolutionContext,
|
|
50
|
+
api: OpenClawPluginApi,
|
|
51
|
+
source: string,
|
|
52
|
+
): string | undefined {
|
|
53
|
+
return resolveWorkspaceDir(api, ctx, { source });
|
|
54
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
2
|
import * as os from 'os';
|
|
3
|
-
import { validateWorkspaceDir, resolveValidWorkspaceDir, logWorkspaceDirHealth } from '../../src/core/workspace-dir-
|
|
3
|
+
import { validateWorkspaceDir, resolveValidWorkspaceDir, logWorkspaceDirHealth } from '../../src/core/workspace-dir-service.js';
|
|
4
4
|
|
|
5
5
|
const homeDir = os.homedir();
|
|
6
6
|
|
|
@@ -65,7 +65,7 @@ describe('E2E: Tool Hooks workspaceDir Resolution', () => {
|
|
|
65
65
|
|
|
66
66
|
describe('Scenario 2: ctx.workspaceDir is undefined (current OpenClaw behavior)', () => {
|
|
67
67
|
it('should fallback to agentId resolution', async () => {
|
|
68
|
-
const { resolveValidWorkspaceDir } = await import('../../src/core/workspace-dir-
|
|
68
|
+
const { resolveValidWorkspaceDir } = await import('../../src/core/workspace-dir-service.js');
|
|
69
69
|
|
|
70
70
|
const mockApi = createMockApi(testWorkspaceDir);
|
|
71
71
|
const ctx = {
|
|
@@ -80,7 +80,7 @@ describe('E2E: Tool Hooks workspaceDir Resolution', () => {
|
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
it('should refuse to guess a workspace when agentId is also undefined', async () => {
|
|
83
|
-
const { resolveValidWorkspaceDir } = await import('../../src/core/workspace-dir-
|
|
83
|
+
const { resolveValidWorkspaceDir } = await import('../../src/core/workspace-dir-service.js');
|
|
84
84
|
|
|
85
85
|
const mockApi = createMockApi(testWorkspaceDir);
|
|
86
86
|
const ctx = {
|
|
@@ -131,7 +131,7 @@ describe('E2E: Tool Hooks workspaceDir Resolution', () => {
|
|
|
131
131
|
|
|
132
132
|
describe('Scenario 4: Invalid workspace candidates are rejected', () => {
|
|
133
133
|
it('should return undefined when all workspace resolution candidates are invalid', async () => {
|
|
134
|
-
const { resolveValidWorkspaceDir } = await import('../../src/core/workspace-dir-
|
|
134
|
+
const { resolveValidWorkspaceDir } = await import('../../src/core/workspace-dir-service.js');
|
|
135
135
|
|
|
136
136
|
const mockApi = createMockApi(os.homedir());
|
|
137
137
|
mockApi.runtime.agent.resolveAgentWorkspaceDir.mockReturnValue(os.homedir());
|
package/vitest.config.ts
CHANGED
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
import { defineConfig } from 'vitest/config';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Vitest configuration with test layering
|
|
5
|
+
*
|
|
6
|
+
* LAYERS:
|
|
7
|
+
* - unit: Mock-based tests, no real DB (fast, parallel)
|
|
8
|
+
* - integration: Tests using real SQLite DB (requires threads pool)
|
|
9
|
+
*
|
|
10
|
+
* USAGE:
|
|
11
|
+
* - npm test → run all tests
|
|
12
|
+
* - npm run test:unit → run unit tests only (fast)
|
|
13
|
+
* - npm run test:integration → run integration tests only
|
|
14
|
+
*
|
|
15
|
+
* WHY threads pool?
|
|
16
|
+
* better-sqlite3 native handles don't clean up properly in fork subprocesses,
|
|
17
|
+
* causing teardown hangs. Threads pool handles this correctly.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// Integration tests: use real SQLite database
|
|
21
|
+
const integrationTests = [
|
|
22
|
+
'tests/core/control-ui-db.test.ts',
|
|
23
|
+
'tests/core/evolution-logger.test.ts',
|
|
24
|
+
'tests/core/nocturnal-e2e.test.ts',
|
|
25
|
+
'tests/core/nocturnal-trajectory-extractor.test.ts',
|
|
26
|
+
'tests/core/replay-engine.test.ts',
|
|
27
|
+
'tests/core/trajectory.test.ts',
|
|
28
|
+
'tests/integration/**/*.test.ts',
|
|
29
|
+
'tests/integration/**/*.test.tsx',
|
|
30
|
+
'tests/service/nocturnal-service-code-candidate.test.ts',
|
|
31
|
+
'tests/service/nocturnal-target-selector.test.ts',
|
|
32
|
+
];
|
|
33
|
+
|
|
3
34
|
export default defineConfig({
|
|
4
35
|
test: {
|
|
5
36
|
environment: 'node',
|
|
6
37
|
include: ['tests/**/*.test.ts', 'tests/**/*.test.tsx'],
|
|
7
|
-
|
|
8
|
-
pool: 'forks',
|
|
9
|
-
// 确保测试完成后进程能正常退出
|
|
38
|
+
pool: 'threads',
|
|
10
39
|
teardownTimeout: 15000,
|
|
11
40
|
coverage: {
|
|
12
41
|
provider: 'v8',
|
|
@@ -18,6 +47,24 @@ export default defineConfig({
|
|
|
18
47
|
branches: 60,
|
|
19
48
|
statements: 70,
|
|
20
49
|
},
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
50
|
+
},
|
|
51
|
+
// Workspace projects for layered testing
|
|
52
|
+
projects: [
|
|
53
|
+
{
|
|
54
|
+
test: {
|
|
55
|
+
name: 'unit',
|
|
56
|
+
include: ['tests/**/*.test.ts', 'tests/**/*.test.tsx'],
|
|
57
|
+
exclude: integrationTests,
|
|
58
|
+
pool: 'threads',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
test: {
|
|
63
|
+
name: 'integration',
|
|
64
|
+
include: integrationTests,
|
|
65
|
+
pool: 'threads',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
});
|