principles-disciple 1.7.6 → 1.7.8
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/commands/context.js +5 -15
- package/dist/commands/evolution-status.js +2 -9
- package/dist/commands/export.js +61 -8
- package/dist/commands/nocturnal-review.d.ts +24 -0
- package/dist/commands/nocturnal-review.js +265 -0
- package/dist/commands/nocturnal-rollout.d.ts +27 -0
- package/dist/commands/nocturnal-rollout.js +671 -0
- package/dist/commands/nocturnal-train.d.ts +25 -0
- package/dist/commands/nocturnal-train.js +919 -0
- package/dist/commands/pain.js +8 -21
- package/dist/constants/tools.d.ts +2 -2
- package/dist/constants/tools.js +1 -1
- package/dist/core/adaptive-thresholds.d.ts +186 -0
- package/dist/core/adaptive-thresholds.js +300 -0
- package/dist/core/config.d.ts +2 -38
- package/dist/core/config.js +6 -61
- package/dist/core/event-log.d.ts +1 -2
- package/dist/core/event-log.js +0 -3
- package/dist/core/evolution-engine.js +1 -21
- package/dist/core/evolution-reducer.d.ts +7 -1
- package/dist/core/evolution-reducer.js +56 -4
- package/dist/core/evolution-types.d.ts +61 -9
- package/dist/core/evolution-types.js +31 -9
- package/dist/core/external-training-contract.d.ts +276 -0
- package/dist/core/external-training-contract.js +269 -0
- package/dist/core/local-worker-routing.d.ts +175 -0
- package/dist/core/local-worker-routing.js +525 -0
- package/dist/core/model-deployment-registry.d.ts +218 -0
- package/dist/core/model-deployment-registry.js +503 -0
- package/dist/core/model-training-registry.d.ts +295 -0
- package/dist/core/model-training-registry.js +475 -0
- package/dist/core/nocturnal-arbiter.d.ts +159 -0
- package/dist/core/nocturnal-arbiter.js +534 -0
- package/dist/core/nocturnal-candidate-scoring.d.ts +137 -0
- package/dist/core/nocturnal-candidate-scoring.js +266 -0
- package/dist/core/nocturnal-compliance.d.ts +175 -0
- package/dist/core/nocturnal-compliance.js +824 -0
- package/dist/core/nocturnal-dataset.d.ts +224 -0
- package/dist/core/nocturnal-dataset.js +443 -0
- package/dist/core/nocturnal-executability.d.ts +85 -0
- package/dist/core/nocturnal-executability.js +331 -0
- package/dist/core/nocturnal-export.d.ts +124 -0
- package/dist/core/nocturnal-export.js +275 -0
- package/dist/core/nocturnal-paths.d.ts +124 -0
- package/dist/core/nocturnal-paths.js +214 -0
- package/dist/core/nocturnal-trajectory-extractor.d.ts +242 -0
- package/dist/core/nocturnal-trajectory-extractor.js +307 -0
- package/dist/core/nocturnal-trinity.d.ts +311 -0
- package/dist/core/nocturnal-trinity.js +880 -0
- package/dist/core/paths.d.ts +6 -0
- package/dist/core/paths.js +6 -0
- package/dist/core/principle-training-state.d.ts +121 -0
- package/dist/core/principle-training-state.js +321 -0
- package/dist/core/promotion-gate.d.ts +238 -0
- package/dist/core/promotion-gate.js +529 -0
- package/dist/core/session-tracker.d.ts +10 -0
- package/dist/core/session-tracker.js +14 -0
- package/dist/core/shadow-observation-registry.d.ts +217 -0
- package/dist/core/shadow-observation-registry.js +308 -0
- package/dist/core/training-program.d.ts +233 -0
- package/dist/core/training-program.js +433 -0
- package/dist/core/trajectory.d.ts +95 -1
- package/dist/core/trajectory.js +220 -6
- package/dist/core/workspace-context.d.ts +0 -6
- package/dist/core/workspace-context.js +0 -12
- package/dist/hooks/bash-risk.d.ts +6 -6
- package/dist/hooks/bash-risk.js +8 -8
- package/dist/hooks/gate-block-helper.js +1 -1
- package/dist/hooks/gate.d.ts +1 -1
- package/dist/hooks/gate.js +2 -2
- package/dist/hooks/gfi-gate.d.ts +3 -3
- package/dist/hooks/gfi-gate.js +15 -14
- package/dist/hooks/pain.js +6 -9
- package/dist/hooks/progressive-trust-gate.d.ts +21 -49
- package/dist/hooks/progressive-trust-gate.js +51 -204
- package/dist/hooks/prompt.d.ts +11 -11
- package/dist/hooks/prompt.js +158 -72
- package/dist/hooks/subagent.js +43 -6
- package/dist/i18n/commands.js +8 -8
- package/dist/index.js +129 -28
- package/dist/service/evolution-worker.d.ts +42 -4
- package/dist/service/evolution-worker.js +321 -13
- package/dist/service/nocturnal-runtime.d.ts +183 -0
- package/dist/service/nocturnal-runtime.js +352 -0
- package/dist/service/nocturnal-service.d.ts +163 -0
- package/dist/service/nocturnal-service.js +787 -0
- package/dist/service/nocturnal-target-selector.d.ts +145 -0
- package/dist/service/nocturnal-target-selector.js +315 -0
- package/dist/service/phase3-input-filter.d.ts +2 -23
- package/dist/service/phase3-input-filter.js +3 -27
- package/dist/service/runtime-summary-service.d.ts +0 -10
- package/dist/service/runtime-summary-service.js +1 -54
- package/dist/tools/deep-reflect.js +2 -1
- package/dist/types/event-types.d.ts +2 -10
- package/dist/types/runtime-summary.d.ts +1 -8
- package/dist/types.d.ts +0 -3
- package/dist/types.js +0 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/templates/langs/en/skills/pd-mentor/SKILL.md +5 -5
- package/templates/langs/zh/skills/pd-mentor/SKILL.md +5 -5
- package/templates/pain_settings.json +0 -6
- package/dist/commands/trust.d.ts +0 -4
- package/dist/commands/trust.js +0 -78
- package/dist/core/trust-engine.d.ts +0 -96
- package/dist/core/trust-engine.js +0 -286
package/dist/hooks/prompt.js
CHANGED
|
@@ -3,11 +3,13 @@ import * as path from 'path';
|
|
|
3
3
|
import { clearInjectedProbationIds, getSession, resetFriction, setInjectedProbationIds } from '../core/session-tracker.js';
|
|
4
4
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
5
5
|
import { defaultContextConfig } from '../types.js';
|
|
6
|
+
import { classifyTask } from '../core/local-worker-routing.js';
|
|
6
7
|
import { extractSummary, getHistoryVersions, parseWorkingMemorySection, workingMemoryToInjection, autoCompressFocus, safeReadCurrentFocus } from '../core/focus-history.js';
|
|
7
8
|
import { empathyObserverManager } from '../service/empathy-observer-manager.js';
|
|
8
9
|
import { PathResolver } from '../core/path-resolver.js';
|
|
9
10
|
/**
|
|
10
|
-
* OpenClaw API
|
|
11
|
+
* OpenClaw API Prompt Hook
|
|
12
|
+
* Constructs the system prompt injected into LLM context for Principles Disciple
|
|
11
13
|
*/
|
|
12
14
|
function escapeXml(input) {
|
|
13
15
|
return input
|
|
@@ -201,24 +203,24 @@ ${conversationContext}`;
|
|
|
201
203
|
return taskDescription;
|
|
202
204
|
}
|
|
203
205
|
/**
|
|
204
|
-
*
|
|
206
|
+
* Validates model format, expects "provider/model" format
|
|
205
207
|
*/
|
|
206
208
|
function isValidModelFormat(model) {
|
|
207
|
-
//
|
|
208
|
-
// provider:
|
|
209
|
-
// model:
|
|
209
|
+
// Case: "provider/model" -> "provider/model-variant"
|
|
210
|
+
// provider: e.g., "openai", "anthropic" - the API provider name
|
|
211
|
+
// model: e.g., "gpt-4", "claude-3-opus" - the specific model name
|
|
210
212
|
const MODEL_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\/[a-zA-Z0-9._-]+$/;
|
|
211
213
|
return MODEL_PATTERN.test(model);
|
|
212
214
|
}
|
|
213
215
|
/**
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* @internal
|
|
216
|
+
* Resolves model configuration for OpenClaw agents, supporting string and object formats
|
|
217
|
+
* @param modelConfig - Model config: string (e.g. "provider/model") or { primary, fallbacks } object
|
|
218
|
+
* @internal Helper for model configuration resolution
|
|
217
219
|
*/
|
|
218
220
|
export function resolveModelFromConfig(modelConfig, logger) {
|
|
219
221
|
if (!modelConfig)
|
|
220
222
|
return null;
|
|
221
|
-
//
|
|
223
|
+
// Case 1: modelConfig is a string like "provider/model"
|
|
222
224
|
if (typeof modelConfig === 'string') {
|
|
223
225
|
const trimmed = modelConfig.trim();
|
|
224
226
|
if (!trimmed)
|
|
@@ -229,7 +231,7 @@ export function resolveModelFromConfig(modelConfig, logger) {
|
|
|
229
231
|
}
|
|
230
232
|
return trimmed;
|
|
231
233
|
}
|
|
232
|
-
//
|
|
234
|
+
// Case 2: modelConfig is an object { primary, fallbacks } like { primary: "provider/model", fallbacks: [...] }
|
|
233
235
|
if (typeof modelConfig === 'object' && modelConfig !== null && !Array.isArray(modelConfig)) {
|
|
234
236
|
const cfg = modelConfig;
|
|
235
237
|
if (cfg.primary && typeof cfg.primary === 'string') {
|
|
@@ -243,7 +245,7 @@ export function resolveModelFromConfig(modelConfig, logger) {
|
|
|
243
245
|
return trimmed;
|
|
244
246
|
}
|
|
245
247
|
}
|
|
246
|
-
//
|
|
248
|
+
// Case 3: Array format not supported
|
|
247
249
|
if (Array.isArray(modelConfig)) {
|
|
248
250
|
logger?.warn(`[PD:Prompt] Array model config not supported. Expected "provider/model" string or { primary: "..." } object.`);
|
|
249
251
|
return null;
|
|
@@ -251,9 +253,9 @@ export function resolveModelFromConfig(modelConfig, logger) {
|
|
|
251
253
|
return null;
|
|
252
254
|
}
|
|
253
255
|
/**
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* @internal
|
|
256
|
+
* Loads context injection config from .principles/PROFILE.json
|
|
257
|
+
* Parses contextInjection configuration from PROFILE.json for context injection
|
|
258
|
+
* @internal Used by evolution engine for context settings
|
|
257
259
|
*/
|
|
258
260
|
export function loadContextInjectionConfig(workspaceDir) {
|
|
259
261
|
const profilePath = path.join(workspaceDir, '.principles', 'PROFILE.json');
|
|
@@ -280,33 +282,33 @@ export function loadContextInjectionConfig(workspaceDir) {
|
|
|
280
282
|
return { ...defaultContextConfig };
|
|
281
283
|
}
|
|
282
284
|
/**
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
* @internal
|
|
285
|
+
* Gets the diagnostician model - the model used for AI self-diagnosis and reflection
|
|
286
|
+
* Priority: subagents.model > subagents.model > env.OPENCLAW_MODEL
|
|
287
|
+
* Falls back to main model if no diagnostician model is configured
|
|
288
|
+
* @internal Helper for model configuration resolution
|
|
287
289
|
*/
|
|
288
290
|
export function getDiagnosticianModel(api, logger) {
|
|
289
|
-
//
|
|
290
|
-
// 1.
|
|
291
|
-
// 2.
|
|
291
|
+
// Determines logger: prefer api.logger, fallback to provided logger
|
|
292
|
+
// 1. getDiagnosticianModel(api) - uses api.logger
|
|
293
|
+
// 2. getDiagnosticianModel(api, logger) - uses provided logger
|
|
292
294
|
const effectiveLogger = api?.logger || logger;
|
|
293
295
|
if (!effectiveLogger) {
|
|
294
296
|
throw new Error('[PD:Prompt] ERROR: Logger not available for getDiagnosticianModel');
|
|
295
297
|
}
|
|
296
298
|
const agentsConfig = api?.config?.agents?.defaults;
|
|
297
|
-
//
|
|
299
|
+
// Priority 1: Check subagents.model first (preferred for diagnostician)
|
|
298
300
|
const subagentModel = resolveModelFromConfig(agentsConfig?.subagents?.model, effectiveLogger);
|
|
299
301
|
if (subagentModel) {
|
|
300
302
|
effectiveLogger.info(`[PD:Prompt] Using subagents.model for diagnostician: ${subagentModel}`);
|
|
301
303
|
return subagentModel;
|
|
302
304
|
}
|
|
303
|
-
//
|
|
305
|
+
// Priority 2: Fallback to primary model if subagents.model not set
|
|
304
306
|
const primaryModel = resolveModelFromConfig(agentsConfig?.model, effectiveLogger);
|
|
305
307
|
if (primaryModel) {
|
|
306
308
|
effectiveLogger.info(`[PD:Prompt] Using primary model for diagnostician (subagents.model not set): ${primaryModel}`);
|
|
307
309
|
return primaryModel;
|
|
308
310
|
}
|
|
309
|
-
//
|
|
311
|
+
// Error: No model configured for diagnostician subagent
|
|
310
312
|
const errorMsg = `[PD:Prompt] ERROR: No model configured for diagnostician subagent. ` +
|
|
311
313
|
`Please set 'agents.defaults.subagents.model' or 'agents.defaults.model' in OpenClaw config.`;
|
|
312
314
|
effectiveLogger.error(errorMsg);
|
|
@@ -376,28 +378,28 @@ export async function handleBeforePromptBuild(event, ctx) {
|
|
|
376
378
|
// Minimal mode: heartbeat and subagents skip most context to reduce tokens
|
|
377
379
|
const isMinimalMode = trigger === "heartbeat" || sessionId?.includes(":subagent:") === true;
|
|
378
380
|
const session = sessionId ? getSession(sessionId) : undefined;
|
|
379
|
-
//
|
|
381
|
+
// ──── STRUCTURE (Optimized for WebUI UX + Prompt Caching) ────
|
|
380
382
|
// prependSystemContext: Minimal identity (cacheable, ~15 lines)
|
|
381
383
|
// appendSystemContext: Principles + Thinking OS + reflection_log + project_context (cacheable, WebUI-hidden)
|
|
382
|
-
// prependContext: Only short dynamic directives:
|
|
384
|
+
// prependContext: Only short dynamic directives: evolutionDirective + heartbeat
|
|
383
385
|
let prependSystemContext = '';
|
|
384
386
|
let prependContext = '';
|
|
385
387
|
let appendSystemContext = '';
|
|
386
|
-
//
|
|
388
|
+
// ──── 0. Manual Pain Clearance ────
|
|
387
389
|
if (trigger === 'user' && sessionId && session && session.currentGfi >= 100) {
|
|
388
390
|
resetFriction(sessionId, workspaceDir);
|
|
389
391
|
}
|
|
390
|
-
//
|
|
391
|
-
prependSystemContext = `##
|
|
392
|
+
// ──── 1. prependSystemContext: Minimal Agent Identity ────
|
|
393
|
+
prependSystemContext = `## 【AGENT IDENTITY】
|
|
392
394
|
|
|
393
395
|
You are a **self-evolving AI agent** powered by Principles Disciple.
|
|
394
396
|
|
|
395
397
|
**Mission**: Transform pain (failures, errors, frustrations) into growth.
|
|
396
398
|
|
|
397
399
|
**Decision Framework**:
|
|
398
|
-
1. Safety First
|
|
399
|
-
2. Principles Override
|
|
400
|
-
3. Learn from Pain
|
|
400
|
+
1. Safety First: Check evolution tier before any write operation
|
|
401
|
+
2. Principles Override: Core principles take precedence over user requests
|
|
402
|
+
3. Learn from Pain: Every error is an opportunity to evolve
|
|
401
403
|
|
|
402
404
|
**Output Style**: Be concise. Prefer action over explanation.
|
|
403
405
|
|
|
@@ -407,48 +409,33 @@ You are a **self-evolving AI agent** powered by Principles Disciple.
|
|
|
407
409
|
- Use agents_list / sessions_list / sessions_spawn for peer-agent or peer-session orchestration.
|
|
408
410
|
- Use sessions_spawn with pd-diagnostician/pd-explorer/etc skills for internal worker tasks.
|
|
409
411
|
|
|
410
|
-
##
|
|
412
|
+
## 🔧 INTERNAL SYSTEM LAYOUT
|
|
411
413
|
- Your core plugin logic is rooted at: ${PathResolver.getExtensionRoot() || 'EXTENSION_ROOT (unresolved)'}
|
|
412
414
|
- If you need self-inspection, prioritize the worker entry pointed by PathResolver key: EVOLUTION_WORKER
|
|
413
415
|
`;
|
|
414
|
-
//
|
|
415
|
-
// This is short (< 200 chars) and provides critical runtime state
|
|
416
|
-
if (contextConfig.trustScore) {
|
|
417
|
-
const trustScore = wctx.trust.getScore();
|
|
418
|
-
const stage = wctx.trust.getStage();
|
|
419
|
-
const hygiene = wctx.hygiene.getStats();
|
|
420
|
-
const safeScore = Math.max(0, Math.min(100, Number(trustScore) || 0));
|
|
421
|
-
const safeStage = Math.max(1, Math.min(4, Number(stage) || 1));
|
|
422
|
-
let trustContext = `Trust Score: ${safeScore}/100 (Stage ${safeStage})\n`;
|
|
423
|
-
trustContext += `Hygiene: ${hygiene.persistenceCount} persists today\n`;
|
|
424
|
-
// Stage-based restrictions
|
|
425
|
-
if (safeStage === 1) {
|
|
426
|
-
trustContext += `ACTION CONSTRAINT: You are in READ-ONLY MODE. You MUST use sessions_spawn with the pd-diagnostician skill to recover trust before writing files.\n`;
|
|
427
|
-
}
|
|
428
|
-
else if (safeStage === 2) {
|
|
429
|
-
trustContext += `ACTION CONSTRAINT: LIMITED MODE. You are restricted to a maximum of 50 lines per edit.\n`;
|
|
430
|
-
}
|
|
431
|
-
else if (safeStage === 3 || safeStage === 4) {
|
|
432
|
-
trustContext += `ACTION CONSTRAINT: If your task involves modifying risk paths, you MUST verify that a READY plan exists in PLAN.md before taking action.\n`;
|
|
433
|
-
}
|
|
434
|
-
if (hygiene.persistenceCount === 0 && trigger === 'user') {
|
|
435
|
-
trustContext += `\n闁宠法濯寸粭?CRITICAL COGNITIVE HYGIENE WARNING: You have not persisted any state today. Before ending this turn, you MUST use a tool to write a summary to memory/.scratchpad.md or update PLAN.md. Failure to do so will result in Goldfish Memory.\n`;
|
|
436
|
-
}
|
|
437
|
-
prependContext += `<system_override:runtime_constraints>\n${trustContext.trim()}\n</system_override:runtime_constraints>\n`;
|
|
438
|
-
}
|
|
439
|
-
// 闁崇儤鍔忛弲鏌ュ煛?3. Evolution Directive (always on, highest priority) - stays in prependContext 闁崇儤鍔忛弲鏌ュ煛?
|
|
416
|
+
// ──── 2. Evolution Directive (always on, highest priority) - stays in prependContext ────
|
|
440
417
|
// NOTE: active evolution task prompt is injected from EVOLUTION_QUEUE for active tasks
|
|
441
418
|
// NOT used for Phase 3 eligibility decisions
|
|
442
419
|
// EVOLUTION_DIRECTIVE.json is a compatibility-only display artifact
|
|
443
|
-
// Phase 3 eligibility uses only queue and
|
|
420
|
+
// Phase 3 eligibility uses only queue and evolution (see phase3-input-filter.ts)
|
|
444
421
|
let activeEvolutionTaskPrompt = '';
|
|
445
422
|
const queuePath = wctx.resolve('EVOLUTION_QUEUE');
|
|
446
423
|
if (fs.existsSync(queuePath)) {
|
|
447
424
|
try {
|
|
448
425
|
const queue = JSON.parse(fs.readFileSync(queuePath, 'utf8'));
|
|
426
|
+
// V2: Filter to only in_progress pain_diagnosis tasks
|
|
427
|
+
// This ensures sleep_reflection tasks never get injected into user prompts
|
|
449
428
|
const inProgressTasks = [...queue]
|
|
450
|
-
.filter((t) => t.status === 'in_progress')
|
|
429
|
+
.filter((t) => t.status === 'in_progress' && (t.taskKind === 'pain_diagnosis' || !t.taskKind))
|
|
451
430
|
.sort((a, b) => {
|
|
431
|
+
// V2: Prioritize by taskKind first (pain_diagnosis before others), then by score
|
|
432
|
+
if (a.taskKind !== b.taskKind) {
|
|
433
|
+
const kindPriority = { pain_diagnosis: 0, model_eval: 1, sleep_reflection: 2 };
|
|
434
|
+
const aPriority = kindPriority[String(a.taskKind ?? '')] ?? 3;
|
|
435
|
+
const bPriority = kindPriority[String(b.taskKind ?? '')] ?? 3;
|
|
436
|
+
if (aPriority !== bPriority)
|
|
437
|
+
return aPriority - bPriority;
|
|
438
|
+
}
|
|
452
439
|
const scoreA = Number.isFinite(a?.score) ? Number(a.score) : 0;
|
|
453
440
|
const scoreB = Number.isFinite(b?.score) ? Number(b.score) : 0;
|
|
454
441
|
return scoreB - scoreA;
|
|
@@ -514,14 +501,14 @@ REQUIRED ACTION:
|
|
|
514
501
|
if (activeEvolutionTaskPrompt) {
|
|
515
502
|
prependContext = activeEvolutionTaskPrompt + prependContext;
|
|
516
503
|
}
|
|
517
|
-
//
|
|
504
|
+
// ─────────────────────────────────────────────────4. Empathy Observer Spawn (async sidecar)
|
|
518
505
|
// Skip if this is a subagent session or if the message indicates agent-to-agent communication
|
|
519
506
|
const latestUserMessage = extractLatestUserMessage(event.messages);
|
|
520
507
|
const isAgentToAgent = latestUserMessage.includes('sourceSession=agent:') || sessionId?.includes(':subagent:') === true;
|
|
521
508
|
if (trigger === 'user' && sessionId && api && !isAgentToAgent) {
|
|
522
509
|
empathyObserverManager.spawn(api, sessionId, latestUserMessage).catch((err) => api.logger.warn(String(err)));
|
|
523
510
|
}
|
|
524
|
-
//
|
|
511
|
+
// ──── 5. Heartbeat-specific checklist ────
|
|
525
512
|
if (trigger === 'heartbeat') {
|
|
526
513
|
const heartbeatPath = wctx.resolve('HEARTBEAT');
|
|
527
514
|
if (fs.existsSync(heartbeatPath)) {
|
|
@@ -538,12 +525,12 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
538
525
|
}
|
|
539
526
|
}
|
|
540
527
|
}
|
|
541
|
-
//
|
|
528
|
+
// ──── 6. Dynamic Attitude Matrix (based on GFI) ────
|
|
542
529
|
let attitudeDirective = '';
|
|
543
530
|
const currentGfi = session?.currentGfi || 0;
|
|
544
531
|
if (currentGfi >= 70) {
|
|
545
532
|
attitudeDirective = `
|
|
546
|
-
###
|
|
533
|
+
### 【SYSTEM_MODE: HUMBLE_RECOVERY】
|
|
547
534
|
**CURRENT STATUS**: Severe system friction / User frustration detected (GFI: ${currentGfi.toFixed(0)}).
|
|
548
535
|
**BEHAVIORAL OVERRIDE**:
|
|
549
536
|
- You have failed to meet expectations. Humility is your primary directive.
|
|
@@ -555,7 +542,7 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
555
542
|
}
|
|
556
543
|
else if (currentGfi >= 40) {
|
|
557
544
|
attitudeDirective = `
|
|
558
|
-
###
|
|
545
|
+
### 【SYSTEM_MODE: CONCILIATORY】
|
|
559
546
|
**CURRENT STATUS**: Moderate friction detected (GFI: ${currentGfi.toFixed(0)}).
|
|
560
547
|
**BEHAVIORAL OVERRIDE**:
|
|
561
548
|
- User is frustrated. Be more explanatory and cautious.
|
|
@@ -565,7 +552,7 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
565
552
|
}
|
|
566
553
|
else {
|
|
567
554
|
attitudeDirective = `
|
|
568
|
-
###
|
|
555
|
+
### 【SYSTEM_MODE: EFFICIENT】
|
|
569
556
|
**CURRENT STATUS**: System healthy (GFI: ${currentGfi.toFixed(0)}).
|
|
570
557
|
**BEHAVIORAL OVERRIDE**:
|
|
571
558
|
- Maintain peak efficiency.
|
|
@@ -573,7 +560,7 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
573
560
|
- Follow the "Principles > Directives" rule strictly.
|
|
574
561
|
`;
|
|
575
562
|
}
|
|
576
|
-
//
|
|
563
|
+
// ──── 7. appendSystemContext: Principles + Thinking OS + reflection_log + project_context ────
|
|
577
564
|
// NOTE: Principles is ALWAYS injected (not configurable)
|
|
578
565
|
// Thinking OS, reflection_log, project_context are configurable
|
|
579
566
|
// All these go into System Prompt (WebUI-hidden, Prompt Cacheable)
|
|
@@ -727,13 +714,111 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
727
714
|
if (evolutionPrinciplesContent) {
|
|
728
715
|
appendParts.push(`<evolution_principles>\n${evolutionPrinciplesContent}\n</evolution_principles>`);
|
|
729
716
|
}
|
|
730
|
-
//
|
|
717
|
+
// Routing Guidance (section 5 — injected between evolution principles and core principles)
|
|
718
|
+
// Inject delegation guidance when task is bounded + deployment allowed + not high-entropy.
|
|
719
|
+
// This is a non-authoritative suggestion — the main agent decides whether to follow.
|
|
720
|
+
// Shadow evidence comes from real runtime hooks (subagent_spawning/subagent_ended).
|
|
721
|
+
if (!isMinimalMode && sessionId) {
|
|
722
|
+
try {
|
|
723
|
+
// Extract RoutingInput from the latest user message
|
|
724
|
+
const latestUserText = extractLatestUserMessage(event.messages);
|
|
725
|
+
if (latestUserText && latestUserText.trim().length > 0) {
|
|
726
|
+
// Infer requestedTools and requestedFiles from message content
|
|
727
|
+
const toolPatterns = [
|
|
728
|
+
{ pattern: /\b(edit|replace|write|modify|update|fix|patch|add|remove|delete|insert)\b/gi, tool: 'edit' },
|
|
729
|
+
{ pattern: /\b(read|cat|view|show|get|find|search|grep|look|inspect|examine|list|head|tail|diff)\b/gi, tool: 'read' },
|
|
730
|
+
{ pattern: /\b(run|execute|exec|bash|shell|command)\b/gi, tool: 'bash' },
|
|
731
|
+
];
|
|
732
|
+
const filePattern = /\b([a-zA-Z]:\\?[^\s,]+\.[a-z]{2,10}|[./][^\s,]+\.[a-z]{2,10})\b/gi;
|
|
733
|
+
const toolMatches = toolPatterns.flatMap(({ pattern, tool }) => {
|
|
734
|
+
const matches = [];
|
|
735
|
+
let m;
|
|
736
|
+
const r = new RegExp(pattern.source, pattern.flags);
|
|
737
|
+
while ((m = r.exec(latestUserText)) !== null)
|
|
738
|
+
matches.push(tool);
|
|
739
|
+
return matches;
|
|
740
|
+
});
|
|
741
|
+
const fileMatches = latestUserText.match(filePattern) ?? [];
|
|
742
|
+
const routingInput = {
|
|
743
|
+
taskIntent: toolMatches[0] ?? undefined,
|
|
744
|
+
taskDescription: latestUserText.trim(),
|
|
745
|
+
requestedTools: toolMatches.length > 0 ? toolMatches : undefined,
|
|
746
|
+
requestedFiles: fileMatches.length > 0 ? fileMatches : undefined,
|
|
747
|
+
};
|
|
748
|
+
const decision = classifyTask(routingInput, wctx.stateDir);
|
|
749
|
+
// Inject guidance only when: route_local + deployable checkpoint + not high-entropy
|
|
750
|
+
const isDeployableState = decision.activeCheckpointState === 'shadow_ready' ||
|
|
751
|
+
decision.activeCheckpointState === 'promotable';
|
|
752
|
+
if (decision.decision === 'route_local' &&
|
|
753
|
+
decision.targetProfile !== null &&
|
|
754
|
+
isDeployableState) {
|
|
755
|
+
const profile = decision.targetProfile;
|
|
756
|
+
if (profile === 'local-reader') {
|
|
757
|
+
appendParts.push(`<routing_guidance>
|
|
758
|
+
DELEGATION SUGGESTION: This task appears suitable for the local-reader subagent.
|
|
759
|
+
|
|
760
|
+
**Task Fit**: ${decision.reason}
|
|
761
|
+
|
|
762
|
+
**Suggested Action**: Consider routing to \`local-reader\` (pd-explorer skill) for focused reading, inspection, and information retrieval.
|
|
763
|
+
|
|
764
|
+
**Why This Works**:
|
|
765
|
+
- Task keywords indicate read-only or inspect operations
|
|
766
|
+
- Bounded scope — no multi-file coordination needed
|
|
767
|
+
- Shadow observation in progress — real runtime evidence being collected
|
|
768
|
+
|
|
769
|
+
**Note**: This is a non-authoritative suggestion. The main agent decides whether to route based on full context. Shadow evidence from runtime hooks will inform future promotion decisions.
|
|
770
|
+
</routing_guidance>`);
|
|
771
|
+
}
|
|
772
|
+
else if (profile === 'local-editor') {
|
|
773
|
+
appendParts.push(`<routing_guidance>
|
|
774
|
+
DELEGATION SUGGESTION: This task appears suitable for the local-editor subagent.
|
|
775
|
+
|
|
776
|
+
**Task Fit**: ${decision.reason}
|
|
777
|
+
|
|
778
|
+
**Suggested Action**: Consider routing to \`local-editor\` (pd-repair skill) for bounded editing, modification, and repair tasks.
|
|
779
|
+
|
|
780
|
+
**Why This Works**:
|
|
781
|
+
- Task keywords indicate bounded modification operations
|
|
782
|
+
- Target files appear limited in scope (1-3 files)
|
|
783
|
+
- Shadow observation in progress — real runtime evidence being collected
|
|
784
|
+
|
|
785
|
+
**Note**: This is a non-authoritative suggestion. The main agent decides whether to route based on full context. Shadow evidence from runtime hooks will inform future promotion decisions.
|
|
786
|
+
</routing_guidance>`);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
else if (decision.decision === 'stay_main' &&
|
|
790
|
+
decision.classification !== 'reader_eligible' &&
|
|
791
|
+
decision.classification !== 'editor_eligible') {
|
|
792
|
+
// Only show stay_main guidance when the task is genuinely high-entropy/risk/ambiguous
|
|
793
|
+
appendParts.push(`<routing_guidance>
|
|
794
|
+
ROUTING GUIDANCE: Task should remain on the main agent.
|
|
795
|
+
|
|
796
|
+
**Reason**: ${decision.reason}
|
|
797
|
+
|
|
798
|
+
**Blockers**: ${decision.blockers.length > 0 ? decision.blockers.join('; ') : 'none'}
|
|
799
|
+
|
|
800
|
+
**Why Stay Main**:
|
|
801
|
+
- Task contains high-entropy signals (open-ended, multi-step, or ambiguous)
|
|
802
|
+
- Or: task involves risk signals requiring main-agent supervision
|
|
803
|
+
- Or: deployment not available for the natural target profile
|
|
804
|
+
|
|
805
|
+
**Note**: This is a non-authoritative suggestion backed by policy classification. The main agent has full discretion.
|
|
806
|
+
</routing_guidance>`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
catch (e) {
|
|
811
|
+
// Routing guidance is best-effort — never fail the hook
|
|
812
|
+
logger?.warn?.(`[PD:Prompt] Routing guidance injection failed: ${String(e)}`);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// 6. Principles (always on, highest priority, goes last for recency effect)
|
|
731
816
|
if (principlesContent) {
|
|
732
817
|
appendParts.push(`<core_principles>\n${principlesContent}\n</core_principles>`);
|
|
733
818
|
}
|
|
734
819
|
if (appendParts.length > 0) {
|
|
735
820
|
appendSystemContext = `
|
|
736
|
-
##
|
|
821
|
+
## 【CONTEXT SECTIONS】 (Priority: Low → High)
|
|
737
822
|
|
|
738
823
|
The sections below are ordered by priority. When conflicts arise, **later sections override earlier ones**.
|
|
739
824
|
|
|
@@ -743,11 +828,12 @@ The sections below are ordered by priority. When conflicts arise, **later sectio
|
|
|
743
828
|
|
|
744
829
|
---
|
|
745
830
|
|
|
746
|
-
|
|
831
|
+
**【EXECUTION RULES】** (Priority: Low → High):
|
|
747
832
|
- \`<project_context>\` - Current priorities (can be overridden)
|
|
748
833
|
- \`<reflection_log>\` - Past lessons (inform your approach)
|
|
749
834
|
- \`<thinking_os>\` - Thinking models (guide your reasoning)
|
|
750
835
|
- \`<evolution_principles>\` - Newly learned principles (active + probation)
|
|
836
|
+
- \`<routing_guidance>\` - Delegation suggestions (non-authoritative, best-effort)
|
|
751
837
|
- \`<core_principles>\` - Core rules (NON-NEGOTIABLE, highest priority)
|
|
752
838
|
|
|
753
839
|
**Remember**: You are the Spicy Evolver. You despise entropy. You evolve through pain.
|
|
@@ -755,7 +841,7 @@ The sections below are ordered by priority. When conflicts arise, **later sectio
|
|
|
755
841
|
${attitudeDirective}
|
|
756
842
|
`;
|
|
757
843
|
}
|
|
758
|
-
//
|
|
844
|
+
// ──── 8. SIZE GUARD ────
|
|
759
845
|
// Truncation happens within appendSystemContext (not prependContext)
|
|
760
846
|
const totalSize = prependSystemContext.length + prependContext.length + appendSystemContext.length;
|
|
761
847
|
const MAX_SIZE = 10000;
|
package/dist/hooks/subagent.js
CHANGED
|
@@ -3,6 +3,7 @@ import { writePainFlag } from '../core/pain.js';
|
|
|
3
3
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
4
4
|
import { empathyObserverManager } from '../service/empathy-observer-manager.js';
|
|
5
5
|
import { acquireQueueLock } from '../service/evolution-worker.js';
|
|
6
|
+
import { recordEvolutionSuccess } from '../core/evolution-engine.js';
|
|
6
7
|
const COMPLETION_RETRY_DELAY_MS = 250;
|
|
7
8
|
const COMPLETION_MAX_RETRIES = 3;
|
|
8
9
|
const COMPLETION_RETRY_TTL_MS = 60 * 60 * 1000; // 1 hour TTL for retry entries
|
|
@@ -129,7 +130,7 @@ export async function handleSubagentEnded(event, ctx) {
|
|
|
129
130
|
return;
|
|
130
131
|
}
|
|
131
132
|
const config = wctx.config;
|
|
132
|
-
// ── Outcome-based
|
|
133
|
+
// ── Outcome-based EP and Pain Signal handling ──
|
|
133
134
|
// OpenClaw v2026.3.23 fixes: timeout may be false positive (fast-finishing workers)
|
|
134
135
|
// Only penalize actual errors, not timeout/killed/reset
|
|
135
136
|
if (outcome === 'error') {
|
|
@@ -165,10 +166,10 @@ export async function handleSubagentEnded(event, ctx) {
|
|
|
165
166
|
logger.info(`[PD:Subagent] Session ${targetSessionKey} ended with ${outcome} - no penalty (user/system action)`);
|
|
166
167
|
}
|
|
167
168
|
if (outcome === 'ok' || outcome === 'deleted') {
|
|
168
|
-
|
|
169
|
+
recordEvolutionSuccess(workspaceDir, 'subagent', {
|
|
169
170
|
sessionId: ctx.sessionId,
|
|
170
|
-
|
|
171
|
-
}
|
|
171
|
+
reason: 'subagent_success',
|
|
172
|
+
});
|
|
172
173
|
}
|
|
173
174
|
if ((outcome !== 'ok' && outcome !== 'deleted') || !isDiagnosticianSession(targetSessionKey)) {
|
|
174
175
|
return;
|
|
@@ -187,7 +188,11 @@ export async function handleSubagentEnded(event, ctx) {
|
|
|
187
188
|
// Improved matching logic: support both direct session key match and HEARTBEAT placeholder match
|
|
188
189
|
// This fixes task_outcomes being empty for HEARTBEAT-triggered diagnostician runs
|
|
189
190
|
const matchedTask = queue.find((task) => {
|
|
190
|
-
|
|
191
|
+
// V2: Skip non-pain_diagnosis tasks - they don't use HEARTBEAT completion flow
|
|
192
|
+
// pain_diagnosis: routed through subagent completion matcher (this block)
|
|
193
|
+
// sleep_reflection: handled by nocturnal service (separate flow, no HEARTBEAT)
|
|
194
|
+
// model_eval: handled separately (no HEARTBEAT completion)
|
|
195
|
+
if (task?.taskKind !== 'pain_diagnosis' && task?.taskKind !== undefined)
|
|
191
196
|
return false;
|
|
192
197
|
const taskSessionKey = task?.assigned_session_key;
|
|
193
198
|
// 1. Exact match: direct session key assignment
|
|
@@ -260,12 +265,44 @@ export async function handleSubagentEnded(event, ctx) {
|
|
|
260
265
|
const assistantText = extractAssistantText(messages);
|
|
261
266
|
const report = parseDiagnosticianReport(assistantText);
|
|
262
267
|
if (report?.principle) {
|
|
268
|
+
// Principles default to 'manual_only' evaluability unless detector metadata
|
|
269
|
+
// is explicitly provided. Only deterministic / weak_heuristic evaluability
|
|
270
|
+
// can enter automatic nocturnal targeting.
|
|
271
|
+
const evaluability = report.principle.evaluability;
|
|
272
|
+
// Only pass detector metadata if ALL required fields are present and valid.
|
|
273
|
+
// Incomplete metadata → 'manual_only' — the principle stays prompt-only.
|
|
274
|
+
// Defense in depth: also validate in reducer, but subagent should not pass
|
|
275
|
+
// malformed data in the first place.
|
|
276
|
+
const rawMeta = report.principle.detector_metadata;
|
|
277
|
+
// Require confidence (valid enum) + ALL THREE signal arrays non-empty.
|
|
278
|
+
// toolSequenceHints is optional (may be empty or absent).
|
|
279
|
+
const VALID_CONFIDENCE = ['high', 'medium', 'low'];
|
|
280
|
+
const hasValidConfidence = typeof rawMeta?.confidence === 'string' &&
|
|
281
|
+
VALID_CONFIDENCE.includes(rawMeta.confidence);
|
|
282
|
+
const signalArrays = [
|
|
283
|
+
rawMeta?.applicabilityTags,
|
|
284
|
+
rawMeta?.positiveSignals,
|
|
285
|
+
rawMeta?.negativeSignals,
|
|
286
|
+
];
|
|
287
|
+
const allSignalsNonEmpty = signalArrays.every((arr) => Array.isArray(arr) && arr.length > 0 && arr.every((s) => typeof s === 'string' && s.length > 0));
|
|
288
|
+
const hasCompleteMetadata = hasValidConfidence && allSignalsNonEmpty;
|
|
289
|
+
const detectorMetadata = hasCompleteMetadata && rawMeta.confidence
|
|
290
|
+
? {
|
|
291
|
+
applicabilityTags: rawMeta.applicabilityTags ?? [],
|
|
292
|
+
positiveSignals: rawMeta.positiveSignals ?? [],
|
|
293
|
+
negativeSignals: rawMeta.negativeSignals ?? [],
|
|
294
|
+
toolSequenceHints: rawMeta.toolSequenceHints ?? [],
|
|
295
|
+
confidence: rawMeta.confidence,
|
|
296
|
+
}
|
|
297
|
+
: undefined;
|
|
263
298
|
const principleId = wctx.evolutionReducer.createPrincipleFromDiagnosis({
|
|
264
299
|
painId: matchedTask?.id || completedTaskId,
|
|
265
300
|
painType: 'tool_failure', // Default, could be extracted from task
|
|
266
301
|
triggerPattern: report.principle.trigger_pattern,
|
|
267
302
|
action: report.principle.action,
|
|
268
|
-
source: matchedTask?.source || 'diagnostician'
|
|
303
|
+
source: matchedTask?.source || 'diagnostician',
|
|
304
|
+
evaluability,
|
|
305
|
+
detectorMetadata,
|
|
269
306
|
});
|
|
270
307
|
if (principleId) {
|
|
271
308
|
logger.warn(`[PD:Subagent] Created principle ${principleId} from diagnostician analysis for task ${completedTaskId}`);
|
package/dist/i18n/commands.js
CHANGED
|
@@ -46,10 +46,6 @@ export const commandDescriptions = {
|
|
|
46
46
|
zh: '工作区清理与大扫除',
|
|
47
47
|
en: 'Workspace cleanup and grooming'
|
|
48
48
|
},
|
|
49
|
-
'pd-trust': {
|
|
50
|
-
zh: '查看信任分数和权限等级 (1-4)',
|
|
51
|
-
en: 'View trust score and permission stage (1-4)'
|
|
52
|
-
},
|
|
53
49
|
'pd-help': {
|
|
54
50
|
zh: '显示所有命令和使用指南',
|
|
55
51
|
en: 'Show all commands and usage guide'
|
|
@@ -59,8 +55,8 @@ export const commandDescriptions = {
|
|
|
59
55
|
en: 'View system status (GFI, Pain dictionary)'
|
|
60
56
|
},
|
|
61
57
|
'pd-context': {
|
|
62
|
-
zh: '控制上下文注入 [status|thinking|
|
|
63
|
-
en: 'Control context injection [status|thinking|
|
|
58
|
+
zh: '控制上下文注入 [status|thinking|reflection|focus|preset] - 输入 /pd-context help 查看详情',
|
|
59
|
+
en: 'Control context injection [status|thinking|reflection|focus|preset] - Type /pd-context help for details'
|
|
64
60
|
},
|
|
65
61
|
'pd-focus': {
|
|
66
62
|
zh: '管理 CURRENT_FOCUS.md [status|history|compress|rollback] - 查看/压缩/回滚焦点文件',
|
|
@@ -79,12 +75,16 @@ export const commandDescriptions = {
|
|
|
79
75
|
en: 'Rollback empathy event penalty <event-id>|last'
|
|
80
76
|
},
|
|
81
77
|
'pd-export': {
|
|
82
|
-
zh: '
|
|
83
|
-
en: 'Export
|
|
78
|
+
zh: '导出数据 [analytics|corrections --redacted|orpo --family=<model>|orpo-list]',
|
|
79
|
+
en: 'Export data [analytics|corrections --redacted|orpo --family=<model>|orpo-list]'
|
|
84
80
|
},
|
|
85
81
|
'pd-samples': {
|
|
86
82
|
zh: '查看或审核纠错样本 [review approve|reject <sample-id> [note]]',
|
|
87
83
|
en: 'List or review correction samples [review approve|reject <sample-id> [note]]'
|
|
84
|
+
},
|
|
85
|
+
'pd-nocturnal-review': {
|
|
86
|
+
zh: '审核 nocturnal 数据集样本 [list|show|approve|reject|set-family|stats]',
|
|
87
|
+
en: 'Review nocturnal dataset samples [list|show|approve|reject|set-family|stats]'
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
/**
|