principles-disciple 1.16.0 → 1.18.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/README.md +13 -5
- package/openclaw.plugin.json +4 -4
- package/package.json +1 -1
- package/src/commands/archive-impl.ts +3 -3
- package/src/commands/capabilities.ts +1 -1
- package/src/commands/context.ts +3 -3
- package/src/commands/disable-impl.ts +1 -1
- package/src/commands/evolution-status.ts +2 -2
- package/src/commands/focus.ts +2 -2
- package/src/commands/nocturnal-train.ts +6 -6
- package/src/commands/pain.ts +4 -4
- package/src/commands/pd-reflect.ts +87 -0
- package/src/commands/rollback-impl.ts +4 -4
- package/src/commands/rollback.ts +2 -2
- package/src/commands/samples.ts +2 -2
- package/src/commands/workflow-debug.ts +1 -1
- package/src/config/errors.ts +1 -1
- package/src/core/adaptive-thresholds.ts +1 -1
- package/src/core/code-implementation-storage.ts +2 -2
- package/src/core/config.ts +1 -1
- package/src/core/diagnostician-task-store.ts +2 -2
- package/src/core/empathy-keyword-matcher.ts +3 -3
- package/src/core/event-log.ts +5 -5
- package/src/core/evolution-engine.ts +4 -4
- package/src/core/evolution-logger.ts +1 -1
- package/src/core/evolution-reducer.ts +3 -3
- package/src/core/evolution-types.ts +5 -5
- package/src/core/external-training-contract.ts +1 -1
- package/src/core/focus-history.ts +14 -14
- package/src/core/hygiene/tracker.ts +1 -1
- package/src/core/init.ts +2 -2
- package/src/core/model-deployment-registry.ts +2 -2
- package/src/core/model-training-registry.ts +2 -2
- package/src/core/nocturnal-arbiter.ts +1 -1
- package/src/core/nocturnal-artificer.ts +2 -2
- package/src/core/nocturnal-candidate-scoring.ts +2 -2
- package/src/core/nocturnal-compliance.ts +4 -3
- package/src/core/nocturnal-dataset.ts +3 -3
- package/src/core/nocturnal-export.ts +4 -4
- package/src/core/nocturnal-rule-implementation-validator.ts +1 -1
- package/src/core/nocturnal-snapshot-contract.ts +112 -0
- package/src/core/nocturnal-trajectory-extractor.ts +7 -5
- package/src/core/nocturnal-trinity.ts +480 -158
- package/src/core/pain-context-extractor.ts +3 -3
- package/src/core/pain.ts +124 -11
- package/src/core/path-resolver.ts +4 -4
- package/src/core/pd-task-reconciler.ts +10 -10
- package/src/core/pd-task-service.ts +1 -1
- package/src/core/pd-task-store.ts +1 -1
- package/src/core/principle-internalization/deprecated-readiness.ts +1 -1
- package/src/core/principle-training-state.ts +2 -2
- package/src/core/principle-tree-ledger.ts +7 -7
- package/src/core/promotion-gate.ts +9 -9
- package/src/core/replay-engine.ts +12 -12
- package/src/core/risk-calculator.ts +1 -1
- package/src/core/rule-host-types.ts +2 -2
- package/src/core/rule-host.ts +5 -5
- package/src/core/schema/db-types.ts +1 -1
- package/src/core/schema/schema-definitions.ts +1 -1
- package/src/core/session-tracker.ts +96 -4
- package/src/core/shadow-observation-registry.ts +3 -3
- package/src/core/system-logger.ts +2 -2
- package/src/core/thinking-os-parser.ts +1 -1
- package/src/core/training-program.ts +2 -2
- package/src/core/trajectory.ts +8 -8
- package/src/core/workspace-context.ts +2 -2
- package/src/core/workspace-dir-service.ts +85 -0
- package/src/core/workspace-dir-validation.ts +30 -107
- package/src/hooks/bash-risk.ts +3 -3
- package/src/hooks/edit-verification.ts +4 -4
- package/src/hooks/gate-block-helper.ts +4 -4
- package/src/hooks/gate.ts +10 -10
- package/src/hooks/gfi-gate.ts +7 -7
- package/src/hooks/lifecycle.ts +2 -2
- package/src/hooks/llm.ts +1 -1
- package/src/hooks/pain.ts +25 -5
- package/src/hooks/progressive-trust-gate.ts +7 -7
- package/src/hooks/prompt.ts +24 -5
- package/src/hooks/subagent.ts +2 -2
- package/src/hooks/thinking-checkpoint.ts +2 -2
- package/src/hooks/trajectory-collector.ts +1 -1
- package/src/http/principles-console-route.ts +14 -6
- package/src/i18n/commands.ts +4 -0
- package/src/index.ts +181 -185
- package/src/service/central-health-service.ts +1 -1
- package/src/service/central-overview-service.ts +3 -3
- package/src/service/evolution-query-service.ts +1 -1
- package/src/service/evolution-worker.ts +221 -109
- package/src/service/health-query-service.ts +27 -17
- package/src/service/monitoring-query-service.ts +3 -3
- package/src/service/nocturnal-runtime.ts +4 -4
- package/src/service/nocturnal-service.ts +40 -23
- package/src/service/nocturnal-target-selector.ts +11 -4
- package/src/service/runtime-summary-service.ts +1 -1
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +1 -1
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +3 -3
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +16 -13
- package/src/service/subagent-workflow/runtime-direct-driver.ts +10 -6
- package/src/service/subagent-workflow/types.ts +4 -4
- package/src/service/subagent-workflow/workflow-manager-base.ts +5 -5
- package/src/service/subagent-workflow/workflow-store.ts +2 -2
- package/src/tools/critique-prompt.ts +2 -3
- package/src/tools/deep-reflect.ts +17 -16
- package/src/tools/model-index.ts +1 -1
- package/src/utils/file-lock.ts +1 -1
- package/src/utils/io.ts +7 -2
- package/src/utils/nlp.ts +1 -1
- package/src/utils/plugin-logger.ts +2 -2
- package/src/utils/retry.ts +3 -2
- package/src/utils/subagent-probe.ts +20 -33
- package/templates/langs/en/skills/pd-pain-signal/SKILL.md +8 -7
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +111 -0
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +1 -1
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +1 -1
- package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +8 -7
- package/templates/pain_settings.json +1 -1
- package/tests/build-artifacts.test.ts +4 -58
- package/tests/commands/pd-reflect.test.ts +49 -0
- package/tests/core/nocturnal-snapshot-contract.test.ts +70 -0
- package/tests/core/pain-auto-repair.test.ts +96 -0
- package/tests/core/pain-integration.test.ts +483 -0
- package/tests/core/pain.test.ts +5 -4
- package/tests/core/workspace-dir-service.test.ts +68 -0
- package/tests/core/workspace-dir-validation.test.ts +56 -192
- package/tests/hooks/pain.test.ts +20 -0
- package/tests/http/principles-console-route.test.ts +42 -20
- package/tests/integration/empathy-workflow-integration.test.ts +1 -2
- package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +9 -17
- package/tests/service/empathy-observer-workflow-manager.test.ts +1 -2
- package/tests/service/evolution-worker.nocturnal.test.ts +118 -109
- package/tests/service/nocturnal-runtime-hardening.test.ts +33 -0
- package/tests/utils/subagent-probe.test.ts +32 -0
package/src/hooks/gate.ts
CHANGED
|
@@ -134,7 +134,7 @@ export function handleBeforeToolCall(
|
|
|
134
134
|
const mutationMatch = /(?:>|>>|sed\s+-i|rm|mv|mkdir|touch|cp)\s+(?:-[a-zA-Z]+\s+)*([^\s;&|<>]+)/.exec(command);
|
|
135
135
|
|
|
136
136
|
if (mutationMatch) {
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
filePath = mutationMatch[1];
|
|
139
139
|
} else {
|
|
140
140
|
const hasRiskPath = profile.risk_paths.some(rp => command.includes(rp));
|
|
@@ -167,30 +167,30 @@ export function handleBeforeToolCall(
|
|
|
167
167
|
action: {
|
|
168
168
|
toolName: event.toolName,
|
|
169
169
|
normalizedPath: relPath,
|
|
170
|
-
|
|
170
|
+
|
|
171
171
|
paramsSummary: _extractParamsSummary(event.params),
|
|
172
172
|
},
|
|
173
173
|
workspace: {
|
|
174
174
|
isRiskPath: risky,
|
|
175
|
-
|
|
175
|
+
|
|
176
176
|
planStatus: _getPlanStatus(ctx.workspaceDir),
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
hasPlanFile: _hasPlanFile(ctx.workspaceDir),
|
|
179
179
|
},
|
|
180
180
|
session: {
|
|
181
181
|
sessionId: ctx.sessionId,
|
|
182
|
-
|
|
182
|
+
|
|
183
183
|
currentGfi: _getCurrentGfi(ctx.sessionId),
|
|
184
|
-
|
|
184
|
+
|
|
185
185
|
recentThinking: _hasRecentThinking(ctx.sessionId),
|
|
186
186
|
},
|
|
187
187
|
evolution: {
|
|
188
|
-
|
|
188
|
+
|
|
189
189
|
epTier: _getEpTier(wctx.workspaceDir),
|
|
190
190
|
},
|
|
191
191
|
derived: {
|
|
192
192
|
estimatedLineChanges: estimateLineChanges({ toolName: event.toolName, params: event.params }),
|
|
193
|
-
|
|
193
|
+
|
|
194
194
|
bashRisk: _getBashRisk(event, profile),
|
|
195
195
|
},
|
|
196
196
|
};
|
|
@@ -337,12 +337,12 @@ function _getEpTier(workspaceDir: string): number {
|
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
|
|
340
|
+
|
|
341
341
|
function _getBashRisk(
|
|
342
342
|
event: PluginHookBeforeToolCallEvent,
|
|
343
343
|
_profile: { risk_paths: string[] }
|
|
344
344
|
): 'safe' | 'normal' | 'dangerous' | 'unknown' {
|
|
345
|
-
|
|
345
|
+
|
|
346
346
|
if (!BASH_TOOLS_SET.has(event.toolName)) return 'unknown';
|
|
347
347
|
try {
|
|
348
348
|
const command = String(event.params.command || event.params.args || '');
|
package/src/hooks/gfi-gate.ts
CHANGED
|
@@ -47,16 +47,16 @@ export interface GfiGateConfig {
|
|
|
47
47
|
/**
|
|
48
48
|
* Internal helper to call the shared block helper with gfi-gate source tag.
|
|
49
49
|
*/
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
function block(
|
|
52
52
|
wctx: WorkspaceContext,
|
|
53
53
|
filePath: string,
|
|
54
54
|
reason: string,
|
|
55
55
|
toolName: string,
|
|
56
56
|
sessionId: string | undefined,
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
logger?: { info?: (message: string) => void; warn?: (message: string) => void; error?: (message: string) => void }
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
): PluginHookBeforeToolCallResult {
|
|
61
61
|
return recordGateBlockAndReturn(wctx, {
|
|
62
62
|
filePath,
|
|
@@ -64,19 +64,19 @@ function block(
|
|
|
64
64
|
toolName,
|
|
65
65
|
sessionId,
|
|
66
66
|
blockSource: 'gfi-gate',
|
|
67
|
-
}, logger ||
|
|
67
|
+
}, logger ||
|
|
68
68
|
{ warn: () => {}, error: () => {} } as const);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
export function checkGfiGate(
|
|
73
73
|
event: PluginHookBeforeToolCallEvent,
|
|
74
74
|
wctx: WorkspaceContext,
|
|
75
75
|
sessionId: string | undefined,
|
|
76
76
|
config: GfiGateConfig,
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
logger?: { info?: (message: string) => void; warn?: (message: string) => void }
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
): PluginHookBeforeToolCallResult | undefined {
|
|
81
81
|
if (!config || config.enabled === false || !sessionId) {
|
|
82
82
|
return undefined;
|
package/src/hooks/lifecycle.ts
CHANGED
|
@@ -122,7 +122,7 @@ export async function extractPainFromSessionFile(sessionFile: string, ctx: Plugi
|
|
|
122
122
|
try {
|
|
123
123
|
rl.close();
|
|
124
124
|
fileStream.destroy();
|
|
125
|
-
} catch (_e) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
125
|
+
} catch (_e) { // eslint-disable-line @typescript-eslint/no-unused-vars -- Reason: intentionally unused - cleanup errors ignored
|
|
126
126
|
// Ignore cleanup errors
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -196,7 +196,7 @@ export async function handleBeforeCompaction(
|
|
|
196
196
|
await extractPainFromSessionFile(event.sessionFile, ctx);
|
|
197
197
|
|
|
198
198
|
// 新增:提取并保存工作记忆
|
|
199
|
-
|
|
199
|
+
|
|
200
200
|
await extractAndSaveWorkingMemory(event.sessionFile, ctx, wctx);
|
|
201
201
|
}
|
|
202
202
|
}
|
package/src/hooks/llm.ts
CHANGED
|
@@ -255,7 +255,7 @@ export function handleLlmOutput(
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
// ═══ Thinking OS: Mental Model Usage Tracking ═══
|
|
258
|
-
|
|
258
|
+
|
|
259
259
|
trackThinkingModelUsage({
|
|
260
260
|
text,
|
|
261
261
|
wctx,
|
package/src/hooks/pain.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import { isRisky, normalizePath } from '../utils/io.js';
|
|
3
3
|
import { normalizeProfile } from '../core/profile.js';
|
|
4
4
|
import { computePainScore, buildPainFlag, writePainFlag, trackPrincipleValue } from '../core/pain.js';
|
|
5
|
-
import { getSession, trackFriction, resetFriction, getInjectedProbationIds, clearInjectedProbationIds } from '../core/session-tracker.js';
|
|
5
|
+
import { getSession, trackFriction, resetFriction, getInjectedProbationIds, clearInjectedProbationIds, type SessionState } from '../core/session-tracker.js';
|
|
6
6
|
import { denoiseError, computeHash } from '../utils/hashing.js';
|
|
7
7
|
import { SystemLogger } from '../core/system-logger.js';
|
|
8
8
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
@@ -10,6 +10,8 @@ import { getEvolutionLogger, createTraceId } from '../core/evolution-logger.js';
|
|
|
10
10
|
import { recordEvolutionSuccess, recordEvolutionFailure } from '../core/evolution-engine.js';
|
|
11
11
|
import type { EvolutionLoopEvent } from '../core/evolution-types.js';
|
|
12
12
|
import type { PluginHookAfterToolCallEvent, PluginHookToolContext, OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
13
|
+
import { validateWorkspaceDir } from '../core/workspace-dir-validation.js';
|
|
14
|
+
import { resolveWorkspaceDir } from '../core/workspace-dir-service.js';
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Interface for tool parameters to avoid 'any'
|
|
@@ -49,7 +51,9 @@ export function handleAfterToolCall(
|
|
|
49
51
|
ctx: PluginHookToolContext & { workspaceDir?: string; pluginConfig?: Record<string, unknown> },
|
|
50
52
|
api?: OpenClawPluginApi
|
|
51
53
|
): void {
|
|
52
|
-
const effectiveWorkspaceDir =
|
|
54
|
+
const effectiveWorkspaceDir = api
|
|
55
|
+
? resolveWorkspaceDir(api, ctx, { source: 'after_tool_call' })
|
|
56
|
+
: validateWorkspaceDir(ctx.workspaceDir) ? undefined : ctx.workspaceDir;
|
|
53
57
|
if (!effectiveWorkspaceDir) {
|
|
54
58
|
return;
|
|
55
59
|
}
|
|
@@ -123,10 +127,10 @@ export function handleAfterToolCall(
|
|
|
123
127
|
const hash = computeHash(denoised);
|
|
124
128
|
|
|
125
129
|
const deltaF = config.get('scores.tool_failure_friction') || 30;
|
|
126
|
-
const updatedState = trackFriction(sessionId, deltaF, hash, effectiveWorkspaceDir);
|
|
130
|
+
const updatedState = trackFriction(sessionId, deltaF, hash, effectiveWorkspaceDir, { source: 'tool_failure' });
|
|
127
131
|
|
|
128
132
|
// ── Trust Engine: Record failure ──
|
|
129
|
-
|
|
133
|
+
|
|
130
134
|
const errorType = extractErrorType(event.error || errorText);
|
|
131
135
|
const filePath = params.file_path || params.path || params.file;
|
|
132
136
|
const relPath = typeof filePath === 'string' ? normalizePath(filePath, effectiveWorkspaceDir) : 'unknown';
|
|
@@ -184,7 +188,23 @@ export function handleAfterToolCall(
|
|
|
184
188
|
clearInjectedProbationIds(sessionId, effectiveWorkspaceDir);
|
|
185
189
|
} else {
|
|
186
190
|
// ── SUCCESS BRANCH ──
|
|
187
|
-
|
|
191
|
+
// Only reduce tool_failure source GFI by 50%, preserve user_empathy and other sources
|
|
192
|
+
// This prevents "read file success" from wiping user frustration signals
|
|
193
|
+
const session = getSession(sessionId);
|
|
194
|
+
const toolFailureGfi = session?.gfiBySource?.['tool_failure'] || 0;
|
|
195
|
+
|
|
196
|
+
let resetState: SessionState;
|
|
197
|
+
if (toolFailureGfi > 0) {
|
|
198
|
+
// Reduce tool_failure source by 50% (relief from successful tool execution)
|
|
199
|
+
const reliefAmount = toolFailureGfi * 0.5;
|
|
200
|
+
resetState = resetFriction(sessionId, effectiveWorkspaceDir, {
|
|
201
|
+
source: 'tool_failure',
|
|
202
|
+
amount: reliefAmount,
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
// No tool_failure GFI to reduce, just get current state
|
|
206
|
+
resetState = session || resetFriction(sessionId, effectiveWorkspaceDir);
|
|
207
|
+
}
|
|
188
208
|
|
|
189
209
|
recordEvolutionSuccess(effectiveWorkspaceDir, event.toolName, {
|
|
190
210
|
sessionId,
|
|
@@ -86,15 +86,15 @@ export function buildEvolutionGateReason(
|
|
|
86
86
|
/**
|
|
87
87
|
* Internal helper to call the shared block helper with progressive-trust-gate source tag.
|
|
88
88
|
*/
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
function block(
|
|
91
91
|
filePath: string,
|
|
92
92
|
reason: string,
|
|
93
93
|
wctx: WorkspaceContext,
|
|
94
94
|
toolName: string,
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
logger: { warn?: (message: string) => void; error?: (message: string) => void },
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
sessionId?: string
|
|
99
99
|
): PluginHookBeforeToolCallResult {
|
|
100
100
|
return recordGateBlockAndReturn(wctx, {
|
|
@@ -119,16 +119,16 @@ function block(
|
|
|
119
119
|
* @param profile - Gate profile containing risk_paths config
|
|
120
120
|
* @returns PluginHookBeforeToolCallResult to block, or undefined to allow
|
|
121
121
|
*/
|
|
122
|
-
|
|
122
|
+
|
|
123
123
|
export function checkProgressiveTrustGate(
|
|
124
124
|
event: PluginHookBeforeToolCallEvent,
|
|
125
125
|
wctx: WorkspaceContext,
|
|
126
126
|
relPath: string,
|
|
127
127
|
risky: boolean,
|
|
128
128
|
lineChanges: number,
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
logger: { warn?: (message: string) => void; error?: (message: string) => void; info?: (message: string) => void },
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
ctx: { workspaceDir?: string; sessionId?: string },
|
|
133
133
|
profile?: { risk_paths: string[]; core_governance_files?: string[] }
|
|
134
134
|
): PluginHookBeforeToolCallResult | void {
|
|
@@ -151,7 +151,7 @@ export function checkProgressiveTrustGate(
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
const currentTier = epDecision.currentTier ?? 1;
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
const tierName = getTierName(currentTier);
|
|
156
156
|
|
|
157
157
|
logger.info?.(`[PD_GATE] EP Gate: Tier ${currentTier} (${tierName}), Tool: ${event.toolName}, Risk: ${risky}, Allowed: ${epDecision.allowed}`);
|
package/src/hooks/prompt.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import type { PluginHookBeforePromptBuildEvent, PluginHookAgentContext, PluginHookBeforePromptBuildResult, PluginLogger, OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
4
|
-
import { clearInjectedProbationIds, getSession, resetFriction, setInjectedProbationIds, trackFriction } from '../core/session-tracker.js';
|
|
4
|
+
import { clearInjectedProbationIds, getSession, resetFriction, setInjectedProbationIds, trackFriction, decayGfi, getGfiDecayElapsed } from '../core/session-tracker.js';
|
|
5
5
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
6
6
|
import type { ContextInjectionConfig} from '../types.js';
|
|
7
7
|
import { defaultContextConfig } from '../types.js';
|
|
@@ -308,6 +308,13 @@ export async function handleBeforePromptBuild(
|
|
|
308
308
|
return;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
// ──── DEBUG: Verify subagent availability in this context ────
|
|
312
|
+
const subagent = ctx.api?.runtime?.subagent;
|
|
313
|
+
logger?.info?.(`[PD:DEBUG:SubagentCheck] trigger=${ctx.trigger}, subagent_exists=${!!subagent}, subagent.run_exists=${!!subagent?.run}`);
|
|
314
|
+
if (subagent?.run) {
|
|
315
|
+
logger?.info?.('[PD:DEBUG:SubagentCheck] run entrypoint is callable');
|
|
316
|
+
}
|
|
317
|
+
|
|
311
318
|
const wctx = WorkspaceContext.fromHookContext(ctx);
|
|
312
319
|
const { trigger, sessionId, api } = ctx;
|
|
313
320
|
if (sessionId) {
|
|
@@ -358,7 +365,7 @@ export async function handleBeforePromptBuild(
|
|
|
358
365
|
// appendSystemContext: Principles + Thinking OS + reflection_log + project_context (cacheable, WebUI-hidden)
|
|
359
366
|
// prependContext: Only short dynamic directives: evolutionDirective + heartbeat
|
|
360
367
|
|
|
361
|
-
|
|
368
|
+
|
|
362
369
|
let prependSystemContext = '';
|
|
363
370
|
let prependContext = '';
|
|
364
371
|
let appendSystemContext = '';
|
|
@@ -612,6 +619,18 @@ The empathy observer subagent handles pain detection independently.
|
|
|
612
619
|
|
|
613
620
|
// ──── 4. Heartbeat-specific checklist ────
|
|
614
621
|
if (trigger === 'heartbeat') {
|
|
622
|
+
// ──── 4a. GFI Time-based Decay ────
|
|
623
|
+
// Apply segmented exponential decay to GFI on each heartbeat
|
|
624
|
+
if (sessionId) {
|
|
625
|
+
const elapsedMinutes = getGfiDecayElapsed(sessionId);
|
|
626
|
+
if (elapsedMinutes >= 1) {
|
|
627
|
+
const decayedState = decayGfi(sessionId, elapsedMinutes);
|
|
628
|
+
if (decayedState) {
|
|
629
|
+
logger?.info?.(`[PD:GFI] Heartbeat decay applied: ${elapsedMinutes}min elapsed, GFI now ${decayedState.currentGfi.toFixed(1)}`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
615
634
|
const heartbeatPath = wctx.resolve('HEARTBEAT');
|
|
616
635
|
if (fs.existsSync(heartbeatPath)) {
|
|
617
636
|
try {
|
|
@@ -628,7 +647,7 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
628
647
|
}
|
|
629
648
|
|
|
630
649
|
// ──── 6. Dynamic Attitude Matrix (based on GFI) ────
|
|
631
|
-
|
|
650
|
+
|
|
632
651
|
let attitudeDirective = '';
|
|
633
652
|
const currentGfi = session?.currentGfi || 0;
|
|
634
653
|
|
|
@@ -853,10 +872,10 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
853
872
|
const filePattern = /\b([a-zA-Z]:\\?[^\s,]+\.[a-z]{2,10}|[./][^\s,]+\.[a-z]{2,10})\b/gi;
|
|
854
873
|
const toolMatches = toolPatterns.flatMap(({ pattern, tool }) => {
|
|
855
874
|
const matches: string[] = [];
|
|
856
|
-
|
|
875
|
+
|
|
857
876
|
let _m;
|
|
858
877
|
const r = new RegExp(pattern.source, pattern.flags);
|
|
859
|
-
/* eslint-disable @typescript-eslint/no-unused-vars
|
|
878
|
+
/* eslint-disable @typescript-eslint/no-unused-vars -- Reason: regex exec side effect used, match variable intentionally unused */
|
|
860
879
|
while ((_m = r.exec(latestUserText)) !== null) matches.push(tool);
|
|
861
880
|
return matches;
|
|
862
881
|
});
|
package/src/hooks/subagent.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type { WorkflowManager } from '../service/subagent-workflow/types.js';
|
|
|
12
12
|
* Factory to create the appropriate WorkflowManager by workflow_type string.
|
|
13
13
|
* Used by the subagent_ended hook to dispatch lifecycle recovery to the right manager.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
function createWorkflowManagerForType(
|
|
17
17
|
workflowType: string,
|
|
18
18
|
workspaceDir: string,
|
|
@@ -23,7 +23,7 @@ function createWorkflowManagerForType(
|
|
|
23
23
|
info: (m: string) => logger.info(String(m)),
|
|
24
24
|
warn: (m: string) => logger.warn(String(m)),
|
|
25
25
|
error: (m: string) => logger.error(String(m)),
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
debug: () => {},
|
|
28
28
|
} as unknown as PluginLogger;
|
|
29
29
|
|
|
@@ -40,12 +40,12 @@ export interface ThinkingCheckpointConfig {
|
|
|
40
40
|
* @param logger - Optional logger for info messages
|
|
41
41
|
* @returns Block result if thinking required, undefined otherwise
|
|
42
42
|
*/
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
export function checkThinkingCheckpoint(
|
|
45
45
|
event: PluginHookBeforeToolCallEvent,
|
|
46
46
|
config: ThinkingCheckpointConfig,
|
|
47
47
|
sessionId: string | undefined,
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
logger?: { info?: (message: string) => void }
|
|
50
50
|
): PluginHookBeforeToolCallResult | undefined {
|
|
51
51
|
const enabled = config.enabled ?? false;
|
|
@@ -228,7 +228,7 @@ export function handleBeforeMessageWrite(
|
|
|
228
228
|
// 提取文本内容
|
|
229
229
|
let content = '';
|
|
230
230
|
if (typeof msg.content === 'string') {
|
|
231
|
-
|
|
231
|
+
|
|
232
232
|
// Reason: msg.content is string | ContentPart[]; destructuring would require renaming in the else branch
|
|
233
233
|
content = msg.content;
|
|
234
234
|
} else if (Array.isArray(msg.content)) {
|
|
@@ -9,6 +9,7 @@ import { TrajectoryRegistry } from '../core/trajectory.js';
|
|
|
9
9
|
import { getCentralDatabase } from '../service/central-database.js';
|
|
10
10
|
import { CentralOverviewService } from '../service/central-overview-service.js';
|
|
11
11
|
import { CentralHealthService } from '../service/central-health-service.js';
|
|
12
|
+
import { resolveRequiredWorkspaceDir } from '../core/workspace-dir-service.js';
|
|
12
13
|
|
|
13
14
|
const ROUTE_PREFIX = '/plugins/principles';
|
|
14
15
|
const API_PREFIX = `${ROUTE_PREFIX}/api`;
|
|
@@ -90,11 +91,11 @@ function serveFile(res: ServerResponse, filePath: string): boolean {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
function createService(api: OpenClawPluginApi): ControlUiQueryService {
|
|
93
|
-
const workspaceDir = api
|
|
94
|
+
const workspaceDir = resolveRequiredWorkspaceDir(api, { agentId: 'main' }, { source: 'principles_console.control_ui', fallbackAgentId: 'main' });
|
|
94
95
|
return new ControlUiQueryService(workspaceDir);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
|
|
98
99
|
function handleApiRoute(
|
|
99
100
|
api: OpenClawPluginApi,
|
|
100
101
|
pathname: string,
|
|
@@ -102,13 +103,20 @@ function handleApiRoute(
|
|
|
102
103
|
res: ServerResponse,
|
|
103
104
|
): Promise<boolean> | boolean {
|
|
104
105
|
// Check authentication for API routes
|
|
105
|
-
|
|
106
|
+
|
|
106
107
|
if (!validateGatewayAuth(req)) {
|
|
107
108
|
json(res, 401, { error: 'unauthorized', message: 'Valid Gateway token required.' });
|
|
108
109
|
return true;
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
|
|
112
|
+
let service: ControlUiQueryService;
|
|
113
|
+
try {
|
|
114
|
+
service = createService(api);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
api.logger.warn(`[PD:ControlUI] Failed to resolve workspace for ${pathname}: ${String(error)}`);
|
|
117
|
+
json(res, 500, { error: 'internal_error', message: String(error) });
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
112
120
|
const url = new URL(req.url || pathname, 'http://127.0.0.1');
|
|
113
121
|
const method = (req.method || 'GET').toUpperCase();
|
|
114
122
|
|
|
@@ -338,7 +346,7 @@ function handleApiRoute(
|
|
|
338
346
|
|
|
339
347
|
// === Evolution API ===
|
|
340
348
|
const evolutionService = () => {
|
|
341
|
-
const workspaceDir = api
|
|
349
|
+
const workspaceDir = resolveRequiredWorkspaceDir(api, { agentId: 'main' }, { source: 'principles_console.evolution', fallbackAgentId: 'main' });
|
|
342
350
|
const trajectory = TrajectoryRegistry.get(workspaceDir);
|
|
343
351
|
return getEvolutionQueryService(trajectory);
|
|
344
352
|
};
|
|
@@ -398,7 +406,7 @@ function handleApiRoute(
|
|
|
398
406
|
|
|
399
407
|
// === Health Query API (v1.1 new endpoints) ===
|
|
400
408
|
const healthService = () => {
|
|
401
|
-
const workspaceDir = api
|
|
409
|
+
const workspaceDir = resolveRequiredWorkspaceDir(api, { agentId: 'main' }, { source: 'principles_console.health', fallbackAgentId: 'main' });
|
|
402
410
|
return new HealthQueryService(workspaceDir);
|
|
403
411
|
};
|
|
404
412
|
|
package/src/i18n/commands.ts
CHANGED
|
@@ -37,6 +37,10 @@ export const commandDescriptions: Record<string, Record<SupportedLanguage, strin
|
|
|
37
37
|
zh: '管理思维模型 [status|propose|audit]',
|
|
38
38
|
en: 'Manage Thinking OS [status|propose|audit]'
|
|
39
39
|
},
|
|
40
|
+
'pd-reflect': {
|
|
41
|
+
zh: '手动触发 Nocturnal 睡眠反射(跳过 idle 检测)',
|
|
42
|
+
en: 'Manually trigger Nocturnal sleep reflection (bypass idle check)'
|
|
43
|
+
},
|
|
40
44
|
'pd-daily': {
|
|
41
45
|
zh: '配置并发送进化日报',
|
|
42
46
|
en: 'Configure and send daily evolution report'
|