principles-disciple 1.194.0 → 1.196.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/dist/core/event-log.js +5 -1
- package/dist/core/system-logger.js +10 -3
- package/dist/core/trajectory.js +7 -2
- package/dist/hooks/after-tool-call-helpers.js +15 -0
- package/dist/utils/file-lock.js +1 -0
- package/dist/utils/io.js +2 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/core/event-log.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import { guardWorkspaceLeak } from '@principles/core/runtime-v2';
|
|
3
4
|
import { createEmptyDailyStats } from '../types/event-types.js';
|
|
4
5
|
import { atomicWriteFileSync } from '../utils/io.js';
|
|
5
6
|
import { redactTelemetryString } from '@principles/core/runtime-v2';
|
|
@@ -18,7 +19,10 @@ export class EventLog {
|
|
|
18
19
|
// painScoreSums map removed (PRI-451 Wave 1.5): it only fed the dead
|
|
19
20
|
// stats.pain.avgScore counter, which is also removed.
|
|
20
21
|
constructor(stateDir, logger) {
|
|
21
|
-
|
|
22
|
+
// Guard against mock-leak state paths (e.g. '/fake/state') that pollute
|
|
23
|
+
// filesystem root on Windows. See workspace-leak-guard.ts.
|
|
24
|
+
const safeStateDir = guardWorkspaceLeak(stateDir);
|
|
25
|
+
this.logsDir = path.join(safeStateDir, 'logs');
|
|
22
26
|
if (!fs.existsSync(this.logsDir)) {
|
|
23
27
|
fs.mkdirSync(this.logsDir, { recursive: true });
|
|
24
28
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { resolvePdPath, PD_FILES } from './paths.js';
|
|
4
|
+
import { guardWorkspaceLeak } from '@principles/core/runtime-v2';
|
|
4
5
|
/**
|
|
5
6
|
* System Logger for Principles Disciple
|
|
6
7
|
*
|
|
@@ -76,8 +77,14 @@ export const SystemLogger = {
|
|
|
76
77
|
log(workspaceDir, eventType, message) {
|
|
77
78
|
if (!workspaceDir)
|
|
78
79
|
return;
|
|
80
|
+
// Guard against mock-leak workspace paths (e.g. '/fake/workspace') that
|
|
81
|
+
// pollute filesystem root on Windows. See workspace-leak-guard.ts.
|
|
82
|
+
const safeWorkspaceDir = guardWorkspaceLeak(workspaceDir);
|
|
79
83
|
try {
|
|
80
|
-
|
|
84
|
+
// PRI-504: per-workspace cache key uses path.resolve(safeWorkspaceDir)
|
|
85
|
+
// so mock-leak paths get distinct cache keys under tmpdir, while
|
|
86
|
+
// legitimate paths use their real resolved form.
|
|
87
|
+
const resolved = path.resolve(safeWorkspaceDir);
|
|
81
88
|
const today = getTodayStr();
|
|
82
89
|
// Check if date changed for THIS workspace - invalidate its cache and run cleanup
|
|
83
90
|
if (cachedLogDates.get(resolved) !== today) {
|
|
@@ -86,13 +93,13 @@ export const SystemLogger = {
|
|
|
86
93
|
// Run cleanup once per day per workspace when date changes
|
|
87
94
|
if (lastCleanupDates.get(resolved) !== today) {
|
|
88
95
|
lastCleanupDates.set(resolved, today);
|
|
89
|
-
cleanupOldLogs(
|
|
96
|
+
cleanupOldLogs(safeWorkspaceDir);
|
|
90
97
|
}
|
|
91
98
|
}
|
|
92
99
|
// Get or create log file path for this workspace
|
|
93
100
|
let logFile = cachedLogFiles.get(resolved);
|
|
94
101
|
if (!logFile) {
|
|
95
|
-
logFile = getSystemLogPath(
|
|
102
|
+
logFile = getSystemLogPath(safeWorkspaceDir, new Date());
|
|
96
103
|
cachedLogFiles.set(resolved, logFile);
|
|
97
104
|
const logDir = path.dirname(logFile);
|
|
98
105
|
if (!fs.existsSync(logDir)) {
|
package/dist/core/trajectory.js
CHANGED
|
@@ -6,6 +6,7 @@ import { withLock } from '../utils/file-lock.js';
|
|
|
6
6
|
import { atomicWriteFileSync } from '../utils/io.js';
|
|
7
7
|
import { resolvePdPath } from './paths.js';
|
|
8
8
|
import { SampleNotFoundError } from '../config/index.js';
|
|
9
|
+
import { guardWorkspaceLeak } from '@principles/core/runtime-v2';
|
|
9
10
|
/**
|
|
10
11
|
* Trajectory database stores HISTORICAL and ANALYTICS data.
|
|
11
12
|
*
|
|
@@ -355,7 +356,9 @@ function applyTrajectorySchema(db) {
|
|
|
355
356
|
* @returns list of created/verified table names and any warnings
|
|
356
357
|
*/
|
|
357
358
|
export function initTrajectorySchema(workspaceDir) {
|
|
358
|
-
|
|
359
|
+
// Guard against mock-leak workspace paths (e.g. '/mock/workspace') that
|
|
360
|
+
// pollute filesystem root on Windows. See workspace-leak-guard.ts.
|
|
361
|
+
const resolvedDir = guardWorkspaceLeak(path.resolve(workspaceDir));
|
|
359
362
|
const stateDir = resolvePdPath(resolvedDir, 'STATE_DIR');
|
|
360
363
|
const blobDir = resolvePdPath(resolvedDir, 'TRAJECTORY_BLOBS_DIR');
|
|
361
364
|
const exportDir = resolvePdPath(resolvedDir, 'EXPORTS_DIR');
|
|
@@ -418,7 +421,9 @@ export class TrajectoryDatabase {
|
|
|
418
421
|
orphanBlobGraceMs;
|
|
419
422
|
db;
|
|
420
423
|
constructor(opts) {
|
|
421
|
-
|
|
424
|
+
// Guard against mock-leak workspace paths (e.g. '/mock/workspace') that
|
|
425
|
+
// pollute filesystem root on Windows. See workspace-leak-guard.ts.
|
|
426
|
+
this.workspaceDir = guardWorkspaceLeak(path.resolve(opts.workspaceDir));
|
|
422
427
|
this.stateDir = resolvePdPath(this.workspaceDir, 'STATE_DIR');
|
|
423
428
|
this.dbPath = resolvePdPath(this.workspaceDir, 'TRAJECTORY_DB');
|
|
424
429
|
this.blobDir = resolvePdPath(this.workspaceDir, 'TRAJECTORY_BLOBS_DIR');
|
|
@@ -338,6 +338,21 @@ export function evaluatePainAdmissionForToolCall(event, observation, outcome, la
|
|
|
338
338
|
? [...WRITE_TOOLS, ...BASH_TOOL_NAMES]
|
|
339
339
|
: WRITE_TOOLS;
|
|
340
340
|
if (!allowedTools.includes(event.toolName) || !outcome.isFailure) {
|
|
341
|
+
// PRI-442 A-09: rc-9-no-silent-fallback. Only emit observability when we
|
|
342
|
+
// are DECLINING an actual failure (Case A). Successful tool calls (happy
|
|
343
|
+
// path) must stay silent — they are not degradation. evaluatePainAdmission
|
|
344
|
+
// is called on every after_tool_call event, so logging the success path
|
|
345
|
+
// would spam SYSTEM_*.log on essentially every successful tool call.
|
|
346
|
+
if (outcome.isFailure) {
|
|
347
|
+
SystemLogger.log(workspaceDir, 'PAIN_ADMISSION_SKIPPED', JSON.stringify({
|
|
348
|
+
hook: 'after_tool_call',
|
|
349
|
+
reason: 'not_a_write_tool_failure',
|
|
350
|
+
tool: event.toolName,
|
|
351
|
+
failureSource: outcome.failureSource,
|
|
352
|
+
sessionId,
|
|
353
|
+
nextAction: 'No pain task created; failure is outside write-tool admission scope. Retry or investigate if unexpected.',
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
341
356
|
return {
|
|
342
357
|
admitted: false,
|
|
343
358
|
stage: 'not_applicable',
|
package/dist/utils/file-lock.js
CHANGED
|
@@ -62,6 +62,7 @@ function tryAcquireLock(lockPath, pid) {
|
|
|
62
62
|
const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL;
|
|
63
63
|
// 使用底层的 openSync 实现真正的原子操作
|
|
64
64
|
// 如果文件已存在,会抛出 EEXIST 错误
|
|
65
|
+
// codeql[js/insecure-temporary-file] — O_EXCL + lockPath alongside target, not in shared os.tmpdir()
|
|
65
66
|
const fd = fs.openSync(lockPath, flags);
|
|
66
67
|
// 写入 PID
|
|
67
68
|
fs.writeSync(fd, String(pid));
|
package/dist/utils/io.js
CHANGED
|
@@ -11,6 +11,8 @@ const RENAME_MAX_RETRIES = 3;
|
|
|
11
11
|
const RENAME_BASE_DELAY_MS = 50;
|
|
12
12
|
export function atomicWriteFileSync(filePath, data) {
|
|
13
13
|
const tmpPath = filePath + '.tmp';
|
|
14
|
+
// codeql[js/insecure-temporary-file] — same-directory temp file, not in shared os.tmpdir()
|
|
15
|
+
// (the '.tmp' suffix prevents partial-write corruption via atomic rename)
|
|
14
16
|
fs.writeFileSync(tmpPath, data, 'utf8');
|
|
15
17
|
let lastError;
|
|
16
18
|
for (let attempt = 0; attempt < RENAME_MAX_RETRIES; attempt++) {
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "principles-disciple",
|
|
3
3
|
"name": "Principles Disciple",
|
|
4
4
|
"description": "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.196.0",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onCapabilities": [
|
|
8
8
|
"hook"
|