principles-disciple 1.80.0 → 1.82.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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/commands/strategy.ts +0 -18
- package/src/i18n/commands.ts +0 -12
- package/src/index.ts +47 -22
- package/src/service/correction-observer-service.ts +200 -0
- package/src/service/evolution-worker.ts +2 -123
- package/templates/langs/en/core/BOOTSTRAP.md +4 -18
- package/templates/langs/en/skills/bootstrap-tools/SKILL.md +1 -1
- package/templates/langs/en/skills/init-strategy/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-mentor/SKILL.md +8 -23
- package/templates/langs/zh/core/BOOTSTRAP.md +2 -15
- package/templates/langs/zh/skills/bootstrap-tools/SKILL.md +1 -1
- package/templates/langs/zh/skills/init-strategy/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-mentor/SKILL.md +7 -22
- package/tests/commands/strategy.test.ts +3 -18
- package/tests/service/correction-observer-service.test.ts +331 -0
- package/tests/service/evolution-worker.correction-observer.test.ts +41 -164
- package/templates/langs/en/skills/ai-sprint-orchestration/EXAMPLES.md +0 -63
- package/templates/langs/en/skills/ai-sprint-orchestration/REFERENCE.md +0 -136
- package/templates/langs/en/skills/ai-sprint-orchestration/SKILL.md +0 -67
- package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +0 -143
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +0 -107
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +0 -107
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +0 -95
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +0 -98
- package/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +0 -58
- package/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +0 -190
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +0 -310
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +0 -683
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +0 -604
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +0 -32
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +0 -707
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +0 -3455
- package/templates/langs/en/skills/evolve-system/SKILL.md +0 -46
- package/templates/langs/en/skills/manage-okr/SKILL.md +0 -96
- package/templates/langs/en/skills/pd-daily/SKILL.md +0 -199
- package/templates/langs/en/skills/pd-grooming/SKILL.md +0 -46
- package/templates/langs/zh/skills/ai-sprint-orchestration/EXAMPLES.md +0 -63
- package/templates/langs/zh/skills/ai-sprint-orchestration/REFERENCE.md +0 -136
- package/templates/langs/zh/skills/ai-sprint-orchestration/SKILL.md +0 -67
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +0 -143
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +0 -107
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +0 -107
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +0 -111
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +0 -95
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +0 -98
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +0 -58
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +0 -190
- package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +0 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +0 -310
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +0 -683
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +0 -604
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +0 -32
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +0 -707
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +0 -3455
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/archive.test.mjs +0 -230
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/contract-enforcement.test.mjs +0 -672
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/decision.test.mjs +0 -1321
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +0 -1435
- package/templates/langs/zh/skills/evolve-system/SKILL.md +0 -46
- package/templates/langs/zh/skills/manage-okr/SKILL.md +0 -109
- package/templates/langs/zh/skills/pd-daily/SKILL.md +0 -283
- package/templates/langs/zh/skills/pd-grooming/SKILL.md +0 -46
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/commands/strategy.ts
CHANGED
|
@@ -19,21 +19,3 @@ export function handleInitStrategy(ctx: PluginCommandContext): PluginCommandResu
|
|
|
19
19
|
`Then update \`${userContextPath}\` with key user preferences discovered.`,
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
export function handleManageOkr(ctx: PluginCommandContext): PluginCommandResult {
|
|
24
|
-
const workspaceDir = resolvePluginCommandWorkspaceDir(ctx, 'strategy:manageOkr');
|
|
25
|
-
const wctx = WorkspaceContext.fromHookContext({ workspaceDir, ...ctx.config });
|
|
26
|
-
|
|
27
|
-
const focusPath = wctx.resolve('CURRENT_FOCUS').replace(workspaceDir, '').replace(/^\/+/, '');
|
|
28
|
-
const weekStatePath = wctx.resolve('WEEK_STATE').replace(workspaceDir, '').replace(/^\/+/, '');
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
text:
|
|
32
|
-
`📊 **OKR Management**\n\n` +
|
|
33
|
-
`The Agent will analyze recent work and align sub-agent objectives.\n\n` +
|
|
34
|
-
`**Instructions for Agent:** Read \`${focusPath}\` and \`${weekStatePath}\`. ` +
|
|
35
|
-
`Compare them against recent session history. ` +
|
|
36
|
-
`Update OKRs as needed and output a brief alignment report. ` +
|
|
37
|
-
`If OKRs are stale (>7 days), prompt the user for a re-alignment conversation.`,
|
|
38
|
-
};
|
|
39
|
-
}
|
package/src/i18n/commands.ts
CHANGED
|
@@ -21,10 +21,6 @@ export const commandDescriptions: Record<string, Record<SupportedLanguage, strin
|
|
|
21
21
|
zh: '初始化工作区(生成 PRINCIPLES.md、THINKING_OS.md 等)',
|
|
22
22
|
en: 'Initialize workspace (generate PRINCIPLES.md, THINKING_OS.md, etc.)'
|
|
23
23
|
},
|
|
24
|
-
'pd-okr': {
|
|
25
|
-
zh: '管理 OKR 目标与关键结果',
|
|
26
|
-
en: 'Manage OKR goals and key results'
|
|
27
|
-
},
|
|
28
24
|
'pd-bootstrap': {
|
|
29
25
|
zh: '扫描环境工具并建议升级',
|
|
30
26
|
en: 'Scan environment tools and suggest upgrades'
|
|
@@ -37,14 +33,6 @@ export const commandDescriptions: Record<string, Record<SupportedLanguage, strin
|
|
|
37
33
|
zh: '管理思维模型 [status|propose|audit](默认关闭,/pd-context thinking on 开启)',
|
|
38
34
|
en: 'Manage Thinking OS [status|propose|audit] (off by default, enable via /pd-context thinking on)'
|
|
39
35
|
},
|
|
40
|
-
'pd-daily': {
|
|
41
|
-
zh: '配置并发送进化日报',
|
|
42
|
-
en: 'Configure and send daily evolution report'
|
|
43
|
-
},
|
|
44
|
-
'pd-grooming': {
|
|
45
|
-
zh: '工作区清理与大扫除',
|
|
46
|
-
en: 'Workspace cleanup and grooming'
|
|
47
|
-
},
|
|
48
36
|
'pd-help': {
|
|
49
37
|
zh: '显示所有命令和使用指南',
|
|
50
38
|
en: 'Show all commands and usage guide'
|
package/src/index.ts
CHANGED
|
@@ -32,7 +32,7 @@ import { handleBeforeReset, handleBeforeCompaction, handleAfterCompaction } from
|
|
|
32
32
|
import { handleLlmOutput } from './hooks/llm.js';
|
|
33
33
|
import { handleSubagentEnded } from './hooks/subagent.js';
|
|
34
34
|
import * as TrajectoryCollector from './hooks/trajectory-collector.js';
|
|
35
|
-
import { handleInitStrategy
|
|
35
|
+
import { handleInitStrategy } from './commands/strategy.js';
|
|
36
36
|
import { handleBootstrapTools, handleResearchTools } from './commands/capabilities.js';
|
|
37
37
|
import { handleThinkingOs } from './commands/thinking-os.js';
|
|
38
38
|
import { handlePainCommand, handlePainReportCommand } from './commands/pain.js';
|
|
@@ -49,6 +49,7 @@ import { handleExportCommand } from './commands/export.js';
|
|
|
49
49
|
import { handleSamplesCommand } from './commands/samples.js';
|
|
50
50
|
import { handleWorkflowDebugCommand } from './commands/workflow-debug.js';
|
|
51
51
|
import { EvolutionWorkerService } from './service/evolution-worker.js';
|
|
52
|
+
import { CorrectionObserverService } from './service/correction-observer-service.js';
|
|
52
53
|
import { TrajectoryService } from './service/trajectory-service.js';
|
|
53
54
|
import { PDTaskService } from './core/pd-task-service.js';
|
|
54
55
|
import { CentralSyncService } from './service/central-sync-service.js';
|
|
@@ -168,6 +169,30 @@ export function shouldStartEvolutionWorker(
|
|
|
168
169
|
return { shouldStart: false, flagSource: flag.source, disabledInfo };
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
export interface CorrectionObserverGateResult {
|
|
173
|
+
shouldStart: boolean;
|
|
174
|
+
flagSource: string;
|
|
175
|
+
disabledInfo: string | null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function shouldStartCorrectionObserver(
|
|
179
|
+
workspaceDir: string,
|
|
180
|
+
logger: { info?: (msg: string) => void; warn?: (msg: string) => void },
|
|
181
|
+
): CorrectionObserverGateResult {
|
|
182
|
+
const flag = loadFeatureFlagFromWorkspace(workspaceDir, 'correction_observer', logger);
|
|
183
|
+
if (flag.enabled) {
|
|
184
|
+
return { shouldStart: true, flagSource: flag.source, disabledInfo: null };
|
|
185
|
+
}
|
|
186
|
+
const disabledInfo = JSON.stringify({
|
|
187
|
+
reason: 'correction_observer_disabled',
|
|
188
|
+
nextAction: 'set correction_observer.enabled=true in .pd/feature-flags.yaml to enable',
|
|
189
|
+
featureFlag: 'correction_observer',
|
|
190
|
+
boundedContext: 'correction_observer_service',
|
|
191
|
+
flagSource: flag.source,
|
|
192
|
+
});
|
|
193
|
+
return { shouldStart: false, flagSource: flag.source, disabledInfo };
|
|
194
|
+
}
|
|
195
|
+
|
|
171
196
|
const plugin = {
|
|
172
197
|
name: "Principles Disciple",
|
|
173
198
|
description: "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
|
|
@@ -246,6 +271,22 @@ const plugin = {
|
|
|
246
271
|
api.logger.info(`[PD] EvolutionWorker NOT started for workspace: ${workspaceDir}. ${gate.disabledInfo}`);
|
|
247
272
|
SystemLogger.log(workspaceDir, 'EVOLUTION_WORKER_DISABLED', gate.disabledInfo ?? '');
|
|
248
273
|
}
|
|
274
|
+
|
|
275
|
+
// ── Start CorrectionObserver for THIS workspace ──
|
|
276
|
+
// MVP-Core per ADR-0014 amendment, independently owned (PRI-293).
|
|
277
|
+
const corrGate = shouldStartCorrectionObserver(workspaceDir, api.logger);
|
|
278
|
+
if (corrGate.shouldStart) {
|
|
279
|
+
CorrectionObserverService.start({
|
|
280
|
+
config: api.config,
|
|
281
|
+
workspaceDir,
|
|
282
|
+
stateDir: path.join(workspaceDir, '.state'),
|
|
283
|
+
logger: api.logger,
|
|
284
|
+
});
|
|
285
|
+
api.logger.info(`[PD] CorrectionObserver started for workspace: ${workspaceDir} (flag source: ${corrGate.flagSource})`);
|
|
286
|
+
} else {
|
|
287
|
+
api.logger.info(`[PD] CorrectionObserver NOT started for workspace: ${workspaceDir}. ${corrGate.disabledInfo}`);
|
|
288
|
+
SystemLogger.log(workspaceDir, 'CORRECTION_OBSERVER_DISABLED', corrGate.disabledInfo ?? '');
|
|
289
|
+
}
|
|
249
290
|
}
|
|
250
291
|
|
|
251
292
|
const result = await handleBeforePromptBuild(event, { ...ctx, api: api as Parameters<typeof handleBeforePromptBuild>[1]['api'], workspaceDir });
|
|
@@ -527,6 +568,8 @@ const plugin = {
|
|
|
527
568
|
EvolutionWorkerService.api = api;
|
|
528
569
|
const guardedEvolutionWorker = guardService('service:evolution-worker', EvolutionWorkerService, api.logger);
|
|
529
570
|
if (guardedEvolutionWorker) api.registerService(guardedEvolutionWorker);
|
|
571
|
+
const guardedCorrectionObserver = guardService('service:correction-observer', CorrectionObserverService, api.logger);
|
|
572
|
+
if (guardedCorrectionObserver) api.registerService(guardedCorrectionObserver);
|
|
530
573
|
const guardedTrajectory = guardService('service:trajectory', TrajectoryService, api.logger);
|
|
531
574
|
if (guardedTrajectory) api.registerService(guardedTrajectory);
|
|
532
575
|
const guardedPdTask = guardService('service:pd-task', PDTaskService, api.logger);
|
|
@@ -560,24 +603,12 @@ const plugin = {
|
|
|
560
603
|
|
|
561
604
|
registerCommandWithAlias('pd-init', 'pdi', getCommandDescription('pd-init', language), (ctx: any) => handleInitStrategy(ctx));
|
|
562
605
|
|
|
563
|
-
registerCommandWithAlias('pd-okr', 'pdk', getCommandDescription('pd-okr', language), (ctx: any) => handleManageOkr(ctx));
|
|
564
|
-
|
|
565
606
|
registerCommandWithAlias('pd-bootstrap', 'pdb', getCommandDescription('pd-bootstrap', language), (ctx: any) => handleBootstrapTools(ctx));
|
|
566
|
-
|
|
607
|
+
|
|
567
608
|
registerCommandWithAlias('pd-research', 'pdr', getCommandDescription('pd-research', language), (ctx: any) => handleResearchTools(ctx));
|
|
568
|
-
|
|
609
|
+
|
|
569
610
|
registerCommandWithAlias('pd-thinking', 'pdt', getCommandDescription('pd-thinking', language), (ctx: any) => handleThinkingOs(ctx), { acceptsArgs: true });
|
|
570
|
-
|
|
571
|
-
registerCommandWithAlias('pd-daily', 'pdd', getCommandDescription('pd-daily', language), () => ({
|
|
572
|
-
text: language === 'zh'
|
|
573
|
-
? "请执行 pd-daily 技能来配置并发送进化日报。系统将引导你完成配置流程,包括发送时间、渠道和报告风格偏好。"
|
|
574
|
-
: "Please execute the pd-daily skill to configure and send your daily evolution report. The system will guide you through the configuration process."
|
|
575
|
-
}));
|
|
576
|
-
registerCommandWithAlias('pd-grooming', 'pdg', getCommandDescription('pd-grooming', language), () => ({
|
|
577
|
-
text: language === 'zh'
|
|
578
|
-
? "请执行 pd-grooming 技能来执行大扫除。例如输入: '执行 pd-grooming 技能'"
|
|
579
|
-
: "Please execute the pd-grooming skill to clean up. For example: 'Execute pd-grooming skill'"
|
|
580
|
-
}));
|
|
611
|
+
|
|
581
612
|
registerCommandWithAlias('pd-help', 'pdh', getCommandDescription('pd-help', language), () => {
|
|
582
613
|
if (language === 'zh') {
|
|
583
614
|
return { text: `
|
|
@@ -593,10 +624,7 @@ const plugin = {
|
|
|
593
624
|
## 状态查询
|
|
594
625
|
| 短命令 | 长命令 | 用途 |
|
|
595
626
|
|--------|--------|------|
|
|
596
|
-
| \`/pdk\` | \`/pd-okr\` | OKR 目标管理 |
|
|
597
627
|
| \`/pdt\` | \`/pd-thinking\` | 思维模型管理 |
|
|
598
|
-
| \`/pdd\` | \`/pd-daily\` | 进化日报 |
|
|
599
|
-
| \`/pdg\` | \`/pd-grooming\` | 工作区清理 |
|
|
600
628
|
|
|
601
629
|
## 其他命令
|
|
602
630
|
| 命令 | 用途 |
|
|
@@ -624,10 +652,7 @@ const plugin = {
|
|
|
624
652
|
## Status
|
|
625
653
|
| Short | Full | Purpose |
|
|
626
654
|
|-------|------|---------|
|
|
627
|
-
| \`/pdk\` | \`/pd-okr\` | OKR goal management |
|
|
628
655
|
| \`/pdt\` | \`/pd-thinking\` | Mental model management |
|
|
629
|
-
| \`/pdd\` | \`/pd-daily\` | Evolution report |
|
|
630
|
-
| \`/pdg\` | \`/pd-grooming\` | Workspace cleanup |
|
|
631
656
|
|
|
632
657
|
## Other Commands
|
|
633
658
|
| Command | Purpose |
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type { OpenClawPluginServiceContext, PluginLogger } from '../openclaw-sdk.js';
|
|
2
|
+
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
3
|
+
import { TrajectoryRegistry } from '../core/trajectory.js';
|
|
4
|
+
import { CorrectionCueLearner } from '../core/correction-cue-learner.js';
|
|
5
|
+
import {
|
|
6
|
+
WorkflowFunnelLoader,
|
|
7
|
+
PiAiRuntimeAdapter,
|
|
8
|
+
CorrectionObserver,
|
|
9
|
+
AgentScheduler,
|
|
10
|
+
} from '@principles/core/runtime-v2';
|
|
11
|
+
import { KeywordOptimizationService } from './keyword-optimization-service.js';
|
|
12
|
+
import { SystemLogger } from '../core/system-logger.js';
|
|
13
|
+
|
|
14
|
+
export interface CorrectionObserverServiceShape {
|
|
15
|
+
id: string;
|
|
16
|
+
start: (ctx: OpenClawPluginServiceContext) => void;
|
|
17
|
+
stop?: (ctx: OpenClawPluginServiceContext) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let correctionObserverTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
21
|
+
let correctionObserverStopped = false;
|
|
22
|
+
const startedWorkspaces = new Set<string>();
|
|
23
|
+
|
|
24
|
+
const CORRECTION_OBSERVER_INTERVAL_MS = 15 * 60 * 1000;
|
|
25
|
+
const CORRECTION_OBSERVER_INITIAL_DELAY_MS = 10_000;
|
|
26
|
+
const CORRECTION_OBSERVER_MAX_RECENT_SESSIONS = 20;
|
|
27
|
+
const CORRECTION_OBSERVER_MAX_PAYLOAD_SESSIONS = 5;
|
|
28
|
+
|
|
29
|
+
export function resolveCorrectionObserver(wctx: WorkspaceContext, logger?: Pick<PluginLogger, 'info' | 'warn' | 'error' | 'debug'>): CorrectionObserver | null {
|
|
30
|
+
try {
|
|
31
|
+
const loader = new WorkflowFunnelLoader(wctx.stateDir);
|
|
32
|
+
const funnel = loader.getFunnel('pd-correction-observer');
|
|
33
|
+
const policy = funnel?.policy;
|
|
34
|
+
if (!policy || policy.runtimeKind !== 'pi-ai') {
|
|
35
|
+
logger?.debug?.('[PD:CorrectionObserver] workflows.yaml pd-correction-observer policy not found. Falling back to environment variables.');
|
|
36
|
+
const provider = process.env.PD_CORRECTION_PROVIDER || 'anthropic';
|
|
37
|
+
const model = process.env.PD_CORRECTION_MODEL || 'anthropic/claude-3-5-sonnet';
|
|
38
|
+
const apiKeyEnv = process.env.PD_CORRECTION_API_KEY_ENV || 'ANTHROPIC_API_KEY';
|
|
39
|
+
const baseUrl = process.env.PD_CORRECTION_BASE_URL;
|
|
40
|
+
|
|
41
|
+
if (!process.env[apiKeyEnv]) {
|
|
42
|
+
logger?.debug?.(`[PD:CorrectionObserver] API key env ${apiKeyEnv} is not set. Periodic optimization disabled.`);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const adapter = new PiAiRuntimeAdapter({
|
|
47
|
+
provider,
|
|
48
|
+
model,
|
|
49
|
+
apiKeyEnv,
|
|
50
|
+
baseUrl,
|
|
51
|
+
workspace: wctx.workspaceDir,
|
|
52
|
+
});
|
|
53
|
+
return new CorrectionObserver({ runtimeAdapter: adapter });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const adapter = new PiAiRuntimeAdapter({
|
|
57
|
+
provider: String(policy.provider),
|
|
58
|
+
model: String(policy.model),
|
|
59
|
+
apiKeyEnv: String(policy.apiKeyEnv),
|
|
60
|
+
maxRetries: policy.maxRetries,
|
|
61
|
+
timeoutMs: policy.timeoutMs ?? 30_000,
|
|
62
|
+
baseUrl: policy.baseUrl,
|
|
63
|
+
workspace: wctx.workspaceDir,
|
|
64
|
+
});
|
|
65
|
+
return new CorrectionObserver({ runtimeAdapter: adapter }, { timeoutMs: policy.timeoutMs });
|
|
66
|
+
} catch (err) {
|
|
67
|
+
logger?.warn?.(`[PD:CorrectionObserver] Failed to resolve CorrectionObserver: ${String(err)}`);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function runCorrectionObserverCycle(wctx: WorkspaceContext, logger: PluginLogger): Promise<void> {
|
|
73
|
+
try {
|
|
74
|
+
const observer = resolveCorrectionObserver(wctx, logger);
|
|
75
|
+
if (!observer) {
|
|
76
|
+
logger?.info?.('[PD:CorrectionObserver] Observer not resolved (no API key or config). Skipping cycle.');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
logger?.info?.('[PD:CorrectionObserver] Observer resolved. Initiating periodic optimization...');
|
|
81
|
+
|
|
82
|
+
const db = TrajectoryRegistry.get(wctx.workspaceDir);
|
|
83
|
+
const recentSessions = db.listRecentSessions({ limit: CORRECTION_OBSERVER_MAX_RECENT_SESSIONS });
|
|
84
|
+
const recentSessionIds = recentSessions.map(s => s.sessionId);
|
|
85
|
+
|
|
86
|
+
if (recentSessionIds.length === 0) {
|
|
87
|
+
logger?.info?.('[PD:CorrectionObserver] No recent sessions found. Skipping correction optimization.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const recentMessages: string[] = [];
|
|
92
|
+
for (const sId of recentSessionIds.slice(0, CORRECTION_OBSERVER_MAX_PAYLOAD_SESSIONS)) {
|
|
93
|
+
try {
|
|
94
|
+
const turns = db.listUserTurnsForSession(sId);
|
|
95
|
+
for (const t of turns) {
|
|
96
|
+
if (t.rawExcerpt) {
|
|
97
|
+
recentMessages.push(t.rawExcerpt);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch (turnErr) {
|
|
101
|
+
logger?.warn?.(`[PD:CorrectionObserver] Failed to load user turns for session ${sId}: ${String(turnErr)}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const learner = CorrectionCueLearner.get(wctx.stateDir);
|
|
106
|
+
const keywords = learner.getStore().keywords;
|
|
107
|
+
const keywordStoreSummary = {
|
|
108
|
+
totalKeywords: keywords.length,
|
|
109
|
+
terms: keywords.map(k => ({
|
|
110
|
+
term: k.term,
|
|
111
|
+
weight: k.weight,
|
|
112
|
+
hitCount: k.hitCount ?? 0,
|
|
113
|
+
truePositiveCount: k.truePositiveCount ?? 0,
|
|
114
|
+
falsePositiveCount: k.falsePositiveCount ?? 0,
|
|
115
|
+
})),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const optimizationService = KeywordOptimizationService.get(wctx.stateDir, wctx.workspaceDir, logger);
|
|
119
|
+
const trajectoryHistory = await optimizationService.buildTrajectoryHistory(recentSessionIds);
|
|
120
|
+
|
|
121
|
+
const payload = {
|
|
122
|
+
parentSessionId: 'correction-observer-service',
|
|
123
|
+
workspaceDir: wctx.workspaceDir,
|
|
124
|
+
keywordStoreSummary,
|
|
125
|
+
recentMessages,
|
|
126
|
+
trajectoryHistory,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const scheduler = new AgentScheduler();
|
|
130
|
+
scheduler.register({
|
|
131
|
+
agentId: 'correction-observer',
|
|
132
|
+
mode: 'realtime',
|
|
133
|
+
runner: observer,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
logger?.info?.(`[PD:CorrectionObserver] Dispatching with ${trajectoryHistory.length} trajectory events, ${recentMessages.length} recent messages.`);
|
|
137
|
+
const result = await scheduler.dispatch('correction-observer', payload);
|
|
138
|
+
logger?.info?.(`[PD:CorrectionObserver] Completed: updated=${result.updated}, summary="${result.summary}"`);
|
|
139
|
+
|
|
140
|
+
if (result.updated) {
|
|
141
|
+
optimizationService.applyResult(result);
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
const errMsg = `Correction observer cycle failed: ${String(err)}`;
|
|
145
|
+
logger?.warn?.(`[PD:CorrectionObserver] ${errMsg}`);
|
|
146
|
+
SystemLogger.log(wctx.workspaceDir, 'CORRECTION_OBSERVER_CYCLE_FAILED', errMsg);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const CorrectionObserverService: CorrectionObserverServiceShape = {
|
|
151
|
+
id: 'principles-correction-observer',
|
|
152
|
+
|
|
153
|
+
start(ctx: OpenClawPluginServiceContext): void {
|
|
154
|
+
const workspaceDir = ctx?.workspaceDir;
|
|
155
|
+
const logger = ctx?.logger || console;
|
|
156
|
+
|
|
157
|
+
if (!workspaceDir) {
|
|
158
|
+
if (logger) logger.warn('[PD:CorrectionObserver] workspaceDir not found in service config. Correction observer disabled.');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (startedWorkspaces.has(workspaceDir)) {
|
|
163
|
+
if (logger) logger.info(`[PD:CorrectionObserver] Already started for workspace: ${workspaceDir}. Skipping duplicate start.`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
startedWorkspaces.add(workspaceDir);
|
|
167
|
+
|
|
168
|
+
correctionObserverStopped = false;
|
|
169
|
+
|
|
170
|
+
const wctx = WorkspaceContext.fromHookContext({ workspaceDir, ...ctx.config });
|
|
171
|
+
if (logger) logger.info(`[PD:CorrectionObserver] Starting with workspaceDir=${wctx.workspaceDir}, stateDir=${wctx.stateDir}`);
|
|
172
|
+
|
|
173
|
+
const interval = CORRECTION_OBSERVER_INTERVAL_MS;
|
|
174
|
+
|
|
175
|
+
async function runCycle(): Promise<void> {
|
|
176
|
+
if (correctionObserverStopped) return;
|
|
177
|
+
await runCorrectionObserverCycle(wctx, logger);
|
|
178
|
+
if (correctionObserverStopped) return;
|
|
179
|
+
correctionObserverTimeoutId = setTimeout(runCycle, interval);
|
|
180
|
+
correctionObserverTimeoutId.unref();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
correctionObserverTimeoutId = setTimeout(() => {
|
|
184
|
+
void runCycle().catch((err) => {
|
|
185
|
+
if (logger) logger.error(`[PD:CorrectionObserver] Startup cycle failed: ${String(err)}`);
|
|
186
|
+
if (correctionObserverStopped) return;
|
|
187
|
+
correctionObserverTimeoutId = setTimeout(runCycle, interval);
|
|
188
|
+
correctionObserverTimeoutId.unref();
|
|
189
|
+
});
|
|
190
|
+
}, CORRECTION_OBSERVER_INITIAL_DELAY_MS);
|
|
191
|
+
correctionObserverTimeoutId.unref();
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
stop(_ctx: OpenClawPluginServiceContext): void {
|
|
195
|
+
correctionObserverStopped = true;
|
|
196
|
+
startedWorkspaces.clear();
|
|
197
|
+
if (correctionObserverTimeoutId) clearTimeout(correctionObserverTimeoutId);
|
|
198
|
+
correctionObserverTimeoutId = null;
|
|
199
|
+
},
|
|
200
|
+
};
|
|
@@ -22,14 +22,7 @@ export { loadEvolutionQueue, saveEvolutionQueue, withQueueLock, acquireQueueLock
|
|
|
22
22
|
export { EVOLUTION_QUEUE_LOCK_SUFFIX, LOCK_MAX_RETRIES, LOCK_RETRY_DELAY_MS, LOCK_STALE_MS } from './queue-io.js';
|
|
23
23
|
import { saveEvolutionQueue, requireQueueLock } from './queue-io.js';
|
|
24
24
|
import { WorkflowStore } from './subagent-workflow/workflow-store.js';
|
|
25
|
-
|
|
26
|
-
WorkflowFunnelLoader,
|
|
27
|
-
PiAiRuntimeAdapter,
|
|
28
|
-
CorrectionObserver,
|
|
29
|
-
AgentScheduler,
|
|
30
|
-
} from '@principles/core/runtime-v2';
|
|
31
|
-
import { KeywordOptimizationService } from './keyword-optimization-service.js';
|
|
32
|
-
import { CorrectionCueLearner } from '../core/correction-cue-learner.js';
|
|
25
|
+
|
|
33
26
|
|
|
34
27
|
import { PrincipleCompiler } from '../core/principle-compiler/index.js';
|
|
35
28
|
import { loadLedger, updatePrinciple } from '../core/principle-tree-ledger.js';
|
|
@@ -632,49 +625,6 @@ async function processEvolutionQueueWithResult(
|
|
|
632
625
|
return { queue: queueResult, errors };
|
|
633
626
|
}
|
|
634
627
|
|
|
635
|
-
function resolveCorrectionObserver(wctx: WorkspaceContext, logger?: Pick<PluginLogger, 'info' | 'warn' | 'error' | 'debug'>): CorrectionObserver | null {
|
|
636
|
-
try {
|
|
637
|
-
const loader = new WorkflowFunnelLoader(wctx.stateDir);
|
|
638
|
-
const funnel = loader.getFunnel('pd-correction-observer');
|
|
639
|
-
const policy = funnel?.policy;
|
|
640
|
-
if (!policy || policy.runtimeKind !== 'pi-ai') {
|
|
641
|
-
logger?.debug?.('[PD:Correction] workflows.yaml pd-correction-observer policy not found. Falling back to environment variables.');
|
|
642
|
-
const provider = process.env.PD_CORRECTION_PROVIDER || 'anthropic';
|
|
643
|
-
const model = process.env.PD_CORRECTION_MODEL || 'anthropic/claude-3-5-sonnet';
|
|
644
|
-
const apiKeyEnv = process.env.PD_CORRECTION_API_KEY_ENV || 'ANTHROPIC_API_KEY';
|
|
645
|
-
const baseUrl = process.env.PD_CORRECTION_BASE_URL;
|
|
646
|
-
|
|
647
|
-
if (!process.env[apiKeyEnv]) {
|
|
648
|
-
logger?.debug?.(`[PD:Correction] Correction observer API key env ${apiKeyEnv} is not set. Periodic optimization disabled.`);
|
|
649
|
-
return null;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
const adapter = new PiAiRuntimeAdapter({
|
|
653
|
-
provider,
|
|
654
|
-
model,
|
|
655
|
-
apiKeyEnv,
|
|
656
|
-
baseUrl,
|
|
657
|
-
workspace: wctx.workspaceDir,
|
|
658
|
-
});
|
|
659
|
-
return new CorrectionObserver({ runtimeAdapter: adapter });
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
const adapter = new PiAiRuntimeAdapter({
|
|
663
|
-
provider: String(policy.provider),
|
|
664
|
-
model: String(policy.model),
|
|
665
|
-
apiKeyEnv: String(policy.apiKeyEnv),
|
|
666
|
-
maxRetries: policy.maxRetries,
|
|
667
|
-
timeoutMs: policy.timeoutMs ?? 30_000,
|
|
668
|
-
baseUrl: policy.baseUrl,
|
|
669
|
-
workspace: wctx.workspaceDir,
|
|
670
|
-
});
|
|
671
|
-
return new CorrectionObserver({ runtimeAdapter: adapter }, { timeoutMs: policy.timeoutMs });
|
|
672
|
-
} catch (err) {
|
|
673
|
-
logger?.warn?.(`[PD:Correction] Failed to resolve CorrectionObserver: ${String(err)}`);
|
|
674
|
-
return null;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
628
|
export const EvolutionWorkerService: ExtendedEvolutionWorkerService = {
|
|
679
629
|
id: 'principles-evolution-worker',
|
|
680
630
|
api: null,
|
|
@@ -759,78 +709,7 @@ export const EvolutionWorkerService: ExtendedEvolutionWorkerService = {
|
|
|
759
709
|
await processDetectionQueue(wctx, api, eventLog);
|
|
760
710
|
}
|
|
761
711
|
// processPromotion removed (D-06) — promotion via PAIN_CANDIDATES no longer needed
|
|
762
|
-
|
|
763
|
-
// ── Correction Observer: periodic keyword optimization (D-40-08 / H-1) ──
|
|
764
|
-
try {
|
|
765
|
-
const observer = resolveCorrectionObserver(wctx, logger);
|
|
766
|
-
if (observer) {
|
|
767
|
-
logger?.info?.('[PD:EvolutionWorker] Correction Observer resolved. Initiating periodic optimization...');
|
|
768
|
-
const db = TrajectoryRegistry.get(wctx.workspaceDir);
|
|
769
|
-
const recentSessions = db.listRecentSessions({ limit: 20 });
|
|
770
|
-
const recentSessionIds = recentSessions.map(s => s.sessionId);
|
|
771
|
-
|
|
772
|
-
if (recentSessionIds.length > 0) {
|
|
773
|
-
const recentMessages: string[] = [];
|
|
774
|
-
for (const sId of recentSessionIds.slice(0, 5)) {
|
|
775
|
-
try {
|
|
776
|
-
const turns = db.listUserTurnsForSession(sId);
|
|
777
|
-
for (const t of turns) {
|
|
778
|
-
if (t.rawExcerpt) {
|
|
779
|
-
recentMessages.push(t.rawExcerpt);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
} catch (turnErr) {
|
|
783
|
-
logger?.warn?.(`[PD:EvolutionWorker] Failed to load user turns for session ${sId}: ${String(turnErr)}`);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
const learner = CorrectionCueLearner.get(wctx.stateDir);
|
|
788
|
-
const keywords = learner.getStore().keywords;
|
|
789
|
-
const keywordStoreSummary = {
|
|
790
|
-
totalKeywords: keywords.length,
|
|
791
|
-
terms: keywords.map(k => ({
|
|
792
|
-
term: k.term,
|
|
793
|
-
weight: k.weight,
|
|
794
|
-
hitCount: k.hitCount ?? 0,
|
|
795
|
-
truePositiveCount: k.truePositiveCount ?? 0,
|
|
796
|
-
falsePositiveCount: k.falsePositiveCount ?? 0,
|
|
797
|
-
})),
|
|
798
|
-
};
|
|
799
|
-
|
|
800
|
-
const optimizationService = KeywordOptimizationService.get(wctx.stateDir, wctx.workspaceDir, logger);
|
|
801
|
-
const trajectoryHistory = await optimizationService.buildTrajectoryHistory(recentSessionIds);
|
|
802
|
-
|
|
803
|
-
const payload = {
|
|
804
|
-
parentSessionId: 'evolution-worker',
|
|
805
|
-
workspaceDir: wctx.workspaceDir,
|
|
806
|
-
keywordStoreSummary,
|
|
807
|
-
recentMessages,
|
|
808
|
-
trajectoryHistory,
|
|
809
|
-
};
|
|
810
|
-
|
|
811
|
-
const scheduler = new AgentScheduler();
|
|
812
|
-
scheduler.register({
|
|
813
|
-
agentId: 'correction-observer',
|
|
814
|
-
mode: 'realtime',
|
|
815
|
-
runner: observer,
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
logger?.info?.(`[PD:EvolutionWorker] Dispatching correction-observer with ${trajectoryHistory.length} trajectory events, ${recentMessages.length} recent messages.`);
|
|
819
|
-
const result = await scheduler.dispatch('correction-observer', payload);
|
|
820
|
-
logger?.info?.(`[PD:EvolutionWorker] Correction-observer completed: updated=${result.updated}, summary="${result.summary}"`);
|
|
821
|
-
|
|
822
|
-
if (result.updated) {
|
|
823
|
-
optimizationService.applyResult(result);
|
|
824
|
-
}
|
|
825
|
-
} else {
|
|
826
|
-
logger?.info?.('[PD:EvolutionWorker] No recent sessions found. Skipping correction optimization.');
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
} catch (corrErr) {
|
|
830
|
-
const corrErrMsg = `Correction observer execution failed: ${String(corrErr)}`;
|
|
831
|
-
cycleResult.errors.push(corrErrMsg);
|
|
832
|
-
logger?.warn?.(`[PD:EvolutionWorker] ${corrErrMsg}`);
|
|
833
|
-
}
|
|
712
|
+
// Correction Observer extracted to independent service (PRI-293) — no longer runs on EvolutionWorker heartbeat
|
|
834
713
|
|
|
835
714
|
try {
|
|
836
715
|
const subagentRuntime = api?.runtime?.subagent;
|
|
@@ -145,12 +145,11 @@ If the user has clear project goals, run `/init-strategy` skill to:
|
|
|
145
145
|
|
|
146
146
|
## ⏰ Cron Jobs Initialization (Recommended)
|
|
147
147
|
|
|
148
|
-
**Why you need this:** Your HEARTBEAT.md has been simplified to reduce token cost. But the tasks that were removed (
|
|
148
|
+
**Why you need this:** Your HEARTBEAT.md has been simplified to reduce token cost. But the tasks that were removed (health checks, strategy alignment, weekly governance) are still important — they just moved here. Without these cron jobs, your workspace will drift from strategy and lose track of weekly progress.
|
|
149
149
|
|
|
150
150
|
**How to install:** Ask the user for permission, then execute the CLI commands below. Each command creates a background task that runs automatically.
|
|
151
151
|
|
|
152
152
|
**What you get:**
|
|
153
|
-
- Clean workspace (no temp file buildup)
|
|
154
153
|
- Early warning on tool failures
|
|
155
154
|
- Automatic strategy alignment
|
|
156
155
|
- Weekly governance with evidence-based task validation
|
|
@@ -158,24 +157,11 @@ If the user has clear project goals, run `/init-strategy` skill to:
|
|
|
158
157
|
|
|
159
158
|
### Ask the User
|
|
160
159
|
|
|
161
|
-
> "I can set up background cron jobs for automatic maintenance:
|
|
160
|
+
> "I can set up background cron jobs for automatic maintenance: health checks, strategy alignment, and weekly governance. Should I configure them?"
|
|
162
161
|
|
|
163
162
|
If the user agrees, **execute these commands:**
|
|
164
163
|
|
|
165
|
-
### 1.
|
|
166
|
-
|
|
167
|
-
**What it does:** Cleans workspace temp files daily to prevent clutter buildup.
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
openclaw cron add --name "pd-grooming-daily" \
|
|
171
|
-
--cron "0 2 * * *" --tz "UTC" \
|
|
172
|
-
--session isolated \
|
|
173
|
-
--light-context \
|
|
174
|
-
--no-deliver \
|
|
175
|
-
--message 'Execute pd-grooming skill: Check workspace root, clean temp files and digital clutter. Follow safety rules strictly. Do not delete business code.'
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### 2. Health Check (Every 4 hours)
|
|
164
|
+
### 1. Health Check (Every 4 hours)
|
|
179
165
|
|
|
180
166
|
**What it does:** Verifies core tools (rg, node, python) are available and alerts on failure.
|
|
181
167
|
|
|
@@ -220,7 +206,7 @@ openclaw cron add --name "weekly-governance" \
|
|
|
220
206
|
--cron "0 0 * * 0" --tz "UTC" \
|
|
221
207
|
--session isolated \
|
|
222
208
|
--timeout 300000 \
|
|
223
|
-
--message 'Execute weekly governance: 1) Validate CURRENT_FOCUS.md claims (PR merged? docs exist? tests pass?), 2) Update WEEK_STATE.json metrics, 3) Record to WEEK_EVENTS.jsonl, 4) If task queue empty,
|
|
209
|
+
--message 'Execute weekly governance: 1) Validate CURRENT_FOCUS.md claims (PR merged? docs exist? tests pass?), 2) Update WEEK_STATE.json metrics, 3) Record to WEEK_EVENTS.jsonl, 4) If task queue empty, remind user to plan next phase tasks'
|
|
224
210
|
```
|
|
225
211
|
|
|
226
212
|
**JSON config reference:**
|
|
@@ -46,7 +46,7 @@ Your goal is to equip the agent team with the most advanced weapons. By analyzin
|
|
|
46
46
|
## Environment Capabilities
|
|
47
47
|
Check @.state/SYSTEM_CAPABILITIES.json for high-performance tools (e.g., ripgrep, ast-grep) available in this environment. Use them!
|
|
48
48
|
```
|
|
49
|
-
- Prompt user to run `/
|
|
49
|
+
- Prompt user to run `/pd-status` or `/admin diagnose` to let Agent perceive new capabilities.
|
|
50
50
|
|
|
51
51
|
## Core Principles
|
|
52
52
|
- **Prefer New Over Old**: Dare to recommend new tools to replace old ones (e.g., recommend `pnpm` over `npm`, `vitest` over `jest`), but explain the reasoning.
|
|
@@ -51,4 +51,4 @@ Based on user's responses, extract 1-3 macro **Objectives (O)**.
|
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
## Completion
|
|
54
|
-
After writing, prompt user: "✅ Strategic anchor locked. Recommend running `/
|
|
54
|
+
After writing, prompt user: "✅ Strategic anchor locked. Recommend running `/pd-status` to check system status, or use `/pd-bootstrap` to scan environment tools."
|