principles-disciple 1.11.0 → 1.13.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/CHANGELOG.md +26 -0
- package/README.md +66 -0
- package/esbuild.config.js +1 -1
- package/openclaw.plugin.json +4 -4
- package/package.json +2 -3
- package/run-nocturnal.mjs +30 -0
- package/scripts/db-migrate.mjs +170 -0
- package/scripts/sync-plugin.mjs +250 -6
- package/src/commands/archive-impl.ts +136 -0
- package/src/commands/capabilities.ts +4 -2
- package/src/commands/context.ts +5 -1
- package/src/commands/disable-impl.ts +151 -0
- package/src/commands/evolution-status.ts +64 -19
- package/src/commands/export.ts +8 -6
- package/src/commands/focus.ts +8 -20
- package/src/commands/nocturnal-review.ts +5 -7
- package/src/commands/nocturnal-rollout.ts +1 -12
- package/src/commands/nocturnal-train.ts +21 -47
- package/src/commands/pain.ts +10 -5
- package/src/commands/principle-rollback.ts +4 -2
- package/src/commands/promote-impl.ts +274 -0
- package/src/commands/rollback-impl.ts +234 -0
- package/src/commands/rollback.ts +6 -3
- package/src/commands/samples.ts +2 -0
- package/src/commands/thinking-os.ts +3 -4
- package/src/commands/workflow-debug.ts +2 -1
- package/src/config/errors.ts +1 -0
- package/src/core/AGENTS.md +34 -0
- package/src/core/adaptive-thresholds.ts +4 -3
- package/src/core/code-implementation-storage.ts +241 -0
- package/src/core/config.ts +5 -2
- package/src/core/control-ui-db.ts +29 -10
- package/src/core/detection-funnel.ts +12 -7
- package/src/core/diagnostician-task-store.ts +156 -0
- package/src/core/dictionary.ts +4 -4
- package/src/core/empathy-keyword-matcher.ts +7 -3
- package/src/core/empathy-types.ts +13 -2
- package/src/core/event-log.ts +14 -6
- package/src/core/evolution-engine.ts +27 -31
- package/src/core/evolution-logger.ts +3 -2
- package/src/core/evolution-reducer.ts +110 -31
- package/src/core/evolution-types.ts +10 -0
- package/src/core/external-training-contract.ts +1 -0
- package/src/core/focus-history.ts +38 -24
- package/src/core/hygiene/tracker.ts +10 -6
- package/src/core/init.ts +5 -2
- package/src/core/migration.ts +3 -3
- package/src/core/model-deployment-registry.ts +6 -4
- package/src/core/model-training-registry.ts +5 -3
- package/src/core/nocturnal-arbiter.ts +13 -14
- package/src/core/nocturnal-artifact-lineage.ts +117 -0
- package/src/core/nocturnal-artificer.ts +257 -0
- package/src/core/nocturnal-candidate-scoring.ts +4 -2
- package/src/core/nocturnal-compliance.ts +67 -19
- package/src/core/nocturnal-dataset.ts +95 -2
- package/src/core/nocturnal-executability.ts +2 -3
- package/src/core/nocturnal-export.ts +6 -3
- package/src/core/nocturnal-rule-implementation-validator.ts +245 -0
- package/src/core/nocturnal-trajectory-extractor.ts +10 -3
- package/src/core/nocturnal-trinity.ts +319 -61
- package/src/core/pain-context-extractor.ts +29 -15
- package/src/core/pain.ts +7 -5
- package/src/core/path-resolver.ts +16 -15
- package/src/core/paths.ts +2 -1
- package/src/core/pd-task-reconciler.ts +463 -0
- package/src/core/pd-task-service.ts +42 -0
- package/src/core/pd-task-store.ts +77 -0
- package/src/core/pd-task-types.ts +128 -0
- package/src/core/principle-internalization/deprecated-readiness.ts +91 -0
- package/src/core/principle-internalization/internalization-routing-policy.ts +208 -0
- package/src/core/principle-internalization/lifecycle-metrics.ts +149 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +243 -0
- package/src/core/principle-internalization/lifecycle-refresh.ts +11 -0
- package/src/core/principle-internalization/principle-lifecycle-service.ts +167 -0
- package/src/core/principle-training-state.ts +95 -370
- package/src/core/principle-tree-ledger.ts +733 -0
- package/src/core/principle-tree-migration.ts +195 -0
- package/src/core/profile.ts +3 -1
- package/src/core/promotion-gate.ts +14 -18
- package/src/core/replay-engine.ts +562 -0
- package/src/core/risk-calculator.ts +6 -4
- package/src/core/rule-host-helpers.ts +39 -0
- package/src/core/rule-host-types.ts +82 -0
- package/src/core/rule-host.ts +245 -0
- package/src/core/rule-implementation-runtime.ts +38 -0
- package/src/core/schema/db-types.ts +16 -0
- package/src/core/schema/index.ts +26 -0
- package/src/core/schema/migration-runner.ts +207 -0
- package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
- package/src/core/schema/migrations/002-init-central.ts +122 -0
- package/src/core/schema/migrations/003-init-workflow.ts +55 -0
- package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
- package/src/core/schema/migrations/index.ts +31 -0
- package/src/core/schema/schema-definitions.ts +650 -0
- package/src/core/session-tracker.ts +6 -4
- package/src/core/shadow-observation-registry.ts +6 -3
- package/src/core/system-logger.ts +2 -2
- package/src/core/thinking-models.ts +182 -46
- package/src/core/thinking-os-parser.ts +156 -0
- package/src/core/training-program.ts +7 -7
- package/src/core/trajectory.ts +42 -36
- package/src/core/workspace-context.ts +77 -11
- package/src/core/workspace-dir-validation.ts +152 -0
- package/src/hooks/AGENTS.md +31 -0
- package/src/hooks/bash-risk.ts +3 -1
- package/src/hooks/edit-verification.ts +9 -5
- package/src/hooks/gate-block-helper.ts +5 -1
- package/src/hooks/gate.ts +152 -5
- package/src/hooks/gfi-gate.ts +9 -2
- package/src/hooks/lifecycle-routing.ts +124 -0
- package/src/hooks/lifecycle.ts +12 -12
- package/src/hooks/llm.ts +17 -109
- package/src/hooks/message-sanitize.ts +5 -3
- package/src/hooks/pain.ts +19 -15
- package/src/hooks/progressive-trust-gate.ts +7 -1
- package/src/hooks/prompt.ts +169 -60
- package/src/hooks/subagent.ts +5 -4
- package/src/hooks/thinking-checkpoint.ts +2 -0
- package/src/hooks/trajectory-collector.ts +15 -12
- package/src/http/principles-console-route.ts +31 -68
- package/src/i18n/commands.ts +2 -2
- package/src/index.ts +130 -40
- package/src/service/central-database.ts +131 -43
- package/src/service/central-health-service.ts +47 -0
- package/src/service/central-overview-service.ts +135 -0
- package/src/service/central-sync-service.ts +87 -0
- package/src/service/control-ui-query-service.ts +46 -36
- package/src/service/event-log-auditor.ts +261 -0
- package/src/service/evolution-query-service.ts +23 -22
- package/src/service/evolution-worker.ts +565 -261
- package/src/service/health-query-service.ts +213 -36
- package/src/service/nocturnal-runtime.ts +8 -4
- package/src/service/nocturnal-service.ts +503 -59
- package/src/service/nocturnal-target-selector.ts +5 -7
- package/src/service/runtime-summary-service.ts +2 -1
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
- package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
- package/src/service/subagent-workflow/index.ts +2 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +169 -284
- package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
- package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
- package/src/service/subagent-workflow/types.ts +9 -4
- package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
- package/src/service/subagent-workflow/workflow-store.ts +71 -11
- package/src/service/trajectory-service.ts +2 -1
- package/src/tools/critique-prompt.ts +1 -1
- package/src/tools/deep-reflect.ts +175 -209
- package/src/tools/model-index.ts +2 -1
- package/src/types/event-types.ts +2 -2
- package/src/types/principle-tree-schema.ts +29 -23
- package/src/utils/file-lock.ts +5 -3
- package/src/utils/io.ts +5 -2
- package/src/utils/nlp.ts +5 -46
- package/src/utils/node-vm-polyfill.ts +11 -0
- package/src/utils/plugin-logger.ts +2 -0
- package/src/utils/retry.ts +572 -0
- package/src/utils/subagent-probe.ts +1 -1
- package/templates/langs/en/core/AGENTS.md +0 -13
- package/templates/langs/en/core/SOUL.md +1 -31
- package/templates/langs/en/core/TOOLS.md +0 -4
- package/templates/langs/en/principles/THINKING_OS.md +77 -0
- package/templates/langs/en/skills/admin/SKILL.md +0 -1
- package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
- package/templates/langs/zh/core/AGENTS.md +0 -22
- package/templates/langs/zh/core/SOUL.md +1 -31
- package/templates/langs/zh/core/TOOLS.md +0 -4
- package/templates/langs/zh/principles/THINKING_OS.md +77 -0
- package/templates/langs/zh/skills/admin/SKILL.md +0 -1
- package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
- package/tests/commands/evolution-status.test.ts +119 -0
- package/tests/commands/implementation-lifecycle.test.ts +362 -0
- package/tests/core/code-implementation-storage.test.ts +398 -0
- package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
- package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
- package/tests/core/nocturnal-artificer.test.ts +241 -0
- package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
- package/tests/core/pd-task-store.test.ts +126 -0
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
- package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
- package/tests/core/principle-training-state.test.ts +228 -1
- package/tests/core/principle-tree-ledger.test.ts +423 -0
- package/tests/core/regression-v1-9-1.test.ts +265 -0
- package/tests/core/replay-engine.test.ts +234 -0
- package/tests/core/rule-host-helpers.test.ts +120 -0
- package/tests/core/rule-host.test.ts +389 -0
- package/tests/core/rule-implementation-runtime.test.ts +64 -0
- package/tests/core/workspace-context.test.ts +53 -0
- package/tests/core/workspace-dir-validation.test.ts +272 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
- package/tests/hooks/pain.test.ts +74 -10
- package/tests/hooks/prompt.test.ts +63 -1
- package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
- package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
- package/tests/service/data-endpoints-regression.test.ts +834 -0
- package/tests/service/evolution-worker.test.ts +0 -123
- package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
- package/tests/utils/nlp.test.ts +1 -19
- package/tests/utils/retry.test.ts +327 -0
- package/ui/src/App.tsx +1 -1
- package/ui/src/api.ts +4 -0
- package/ui/src/charts.tsx +366 -0
- package/ui/src/components/WorkspaceConfig.tsx +107 -75
- package/ui/src/i18n/ui.ts +89 -31
- package/ui/src/pages/EvolutionPage.tsx +1 -1
- package/ui/src/pages/OverviewPage.tsx +441 -81
- package/ui/src/pages/ThinkingModelsPage.tsx +287 -69
- package/ui/src/styles.css +43 -0
- package/ui/src/types.ts +17 -1
- package/src/agents/nocturnal-dreamer.md +0 -152
- package/src/agents/nocturnal-philosopher.md +0 -138
- package/src/agents/nocturnal-reflector.md +0 -126
- package/src/agents/nocturnal-scribe.md +0 -164
- package/templates/workspace/.principles/00-kernel.md +0 -51
- package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
- package/templates/workspace/.principles/PRINCIPLES.md +0 -20
- package/templates/workspace/.principles/PROFILE.json +0 -54
- package/templates/workspace/.principles/PROFILE.schema.json +0 -56
- package/templates/workspace/.principles/THINKING_OS.md +0 -64
- package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
- package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
- package/templates/workspace/.principles/models/_INDEX.md +0 -27
- package/templates/workspace/.principles/models/first_principles.md +0 -62
- package/templates/workspace/.principles/models/marketing_4p.md +0 -52
- package/templates/workspace/.principles/models/porter_five.md +0 -63
- package/templates/workspace/.principles/models/swot.md +0 -60
- package/templates/workspace/.principles/models/user_story_map.md +0 -63
- package/templates/workspace/.state/WORKBOARD.json +0 -4
package/src/hooks/prompt.ts
CHANGED
|
@@ -3,11 +3,13 @@ import * as path from 'path';
|
|
|
3
3
|
import type { PluginHookBeforePromptBuildEvent, PluginHookAgentContext, PluginHookBeforePromptBuildResult, PluginLogger, OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
4
4
|
import { clearInjectedProbationIds, getSession, resetFriction, setInjectedProbationIds, trackFriction } from '../core/session-tracker.js';
|
|
5
5
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
6
|
-
import { ContextInjectionConfig
|
|
6
|
+
import type { ContextInjectionConfig} from '../types.js';
|
|
7
|
+
import { defaultContextConfig } from '../types.js';
|
|
7
8
|
import { classifyTask, type RoutingInput } from '../core/local-worker-routing.js';
|
|
8
9
|
import { extractSummary, getHistoryVersions, parseWorkingMemorySection, workingMemoryToInjection, autoCompressFocus, safeReadCurrentFocus } from '../core/focus-history.js';
|
|
9
|
-
import { EmpathyObserverWorkflowManager, empathyObserverWorkflowSpec } from '../service/subagent-workflow/index.js';
|
|
10
|
+
import { EmpathyObserverWorkflowManager, empathyObserverWorkflowSpec, isExpectedSubagentError } from '../service/subagent-workflow/index.js';
|
|
10
11
|
import { PathResolver } from '../core/path-resolver.js';
|
|
12
|
+
import { isSubagentRuntimeAvailable } from '../utils/subagent-probe.js';
|
|
11
13
|
import {
|
|
12
14
|
matchEmpathyKeywords,
|
|
13
15
|
loadKeywordStore,
|
|
@@ -29,16 +31,6 @@ interface ModelConfigObject {
|
|
|
29
31
|
fallbacks?: string[];
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
/**
|
|
33
|
-
* OpenClaw agents model configuration with subagent model override support
|
|
34
|
-
*/
|
|
35
|
-
interface AgentsModelConfig {
|
|
36
|
-
model?: unknown;
|
|
37
|
-
subagents?: {
|
|
38
|
-
model?: unknown;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
34
|
/**
|
|
43
35
|
* Default model configuration for OpenClaw agents
|
|
44
36
|
*/
|
|
@@ -64,21 +56,6 @@ function escapeXml(input: string): string {
|
|
|
64
56
|
.replace(/'/g, ''');
|
|
65
57
|
}
|
|
66
58
|
|
|
67
|
-
function extractContextSignals(context: { toolName?: string; filePath?: string; userMessage?: string; }): string[] {
|
|
68
|
-
const signals: string[] = [];
|
|
69
|
-
if (context.filePath?.endsWith('.ts')) signals.push('typescript');
|
|
70
|
-
if (context.filePath?.endsWith('.md')) signals.push('markdown');
|
|
71
|
-
if (context.toolName && ['edit', 'replace', 'write', 'write_file', 'apply_patch'].includes(context.toolName)) signals.push('edit');
|
|
72
|
-
if (context.toolName && ['run_shell_command', 'bash'].includes(context.toolName)) signals.push('shell');
|
|
73
|
-
if (context.toolName) signals.push(context.toolName);
|
|
74
|
-
const msg = (context.userMessage || '').toLowerCase();
|
|
75
|
-
if (msg.includes('.ts') || msg.includes('typescript')) signals.push('typescript');
|
|
76
|
-
if (msg.includes('.md') || msg.includes('markdown')) signals.push('markdown');
|
|
77
|
-
if (msg.includes('edit') || msg.includes('write') || msg.includes('patch')) signals.push('edit');
|
|
78
|
-
if (msg.includes('shell') || msg.includes('bash')) signals.push('shell');
|
|
79
|
-
return signals;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
59
|
interface PromptHookApi {
|
|
83
60
|
config?: {
|
|
84
61
|
agents?: {
|
|
@@ -211,7 +188,7 @@ export function loadContextInjectionConfig(workspaceDir: string): ContextInjecti
|
|
|
211
188
|
}
|
|
212
189
|
} catch (e) {
|
|
213
190
|
// Failed to load config — continue with defaults, but log for diagnostics
|
|
214
|
-
|
|
191
|
+
|
|
215
192
|
console.warn(`[PD:Prompt] Failed to load contextInjection config: ${String(e)}`);
|
|
216
193
|
}
|
|
217
194
|
|
|
@@ -257,37 +234,82 @@ export function getDiagnosticianModel(api: PromptHookApi | null, logger?: Plugin
|
|
|
257
234
|
throw new Error(errorMsg);
|
|
258
235
|
}
|
|
259
236
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Extract recent user messages for keyword optimization context.
|
|
239
|
+
*/
|
|
240
|
+
function extractRecentMessages(messages: unknown[] | undefined, limit: number): string[] {
|
|
241
|
+
if (!Array.isArray(messages)) return [];
|
|
242
|
+
const userMessages: string[] = [];
|
|
243
|
+
|
|
244
|
+
for (let i = messages.length - 1; i >= 0 && userMessages.length < limit; i--) {
|
|
264
245
|
const msg = messages[i] as { role?: string; content?: unknown };
|
|
265
246
|
if (msg?.role !== 'user') continue;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
247
|
+
|
|
248
|
+
let text = '';
|
|
249
|
+
if (typeof msg.content === 'string') {
|
|
250
|
+
text = msg.content;
|
|
251
|
+
} else if (Array.isArray(msg.content)) {
|
|
252
|
+
text = msg.content
|
|
253
|
+
.filter((part: unknown) => part && typeof part === 'object' && (part as { type?: string }).type === 'text' && typeof (part as { text?: unknown }).text === 'string')
|
|
254
|
+
.map((part: unknown) => (part as { text: string }).text)
|
|
272
255
|
.join('\n')
|
|
273
256
|
.trim();
|
|
274
|
-
if (text) return text;
|
|
275
257
|
}
|
|
258
|
+
if (text) userMessages.unshift(text.substring(0, 500));
|
|
276
259
|
}
|
|
260
|
+
|
|
261
|
+
return userMessages;
|
|
262
|
+
}
|
|
277
263
|
|
|
278
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Build prompt for keyword optimization subagent.
|
|
266
|
+
*/
|
|
267
|
+
function buildOptimizationPrompt(
|
|
268
|
+
keywordStore: ReturnType<typeof loadKeywordStore>,
|
|
269
|
+
recentMessages: string[],
|
|
270
|
+
): string {
|
|
271
|
+
const currentTerms = Object.entries(keywordStore.terms)
|
|
272
|
+
.map(([term, entry]) => ` - "${term}": weight=${entry.weight}, hits=${entry.hitCount || 0}, fp_rate=${entry.falsePositiveRate?.toFixed(2) || '0.10'}`)
|
|
273
|
+
.join('\n');
|
|
274
|
+
|
|
275
|
+
return `You are an empathy keyword optimizer.
|
|
276
|
+
|
|
277
|
+
## TASK
|
|
278
|
+
Analyze recent user messages and the current empathy keyword store.
|
|
279
|
+
Return STRICT JSON (no markdown):
|
|
280
|
+
|
|
281
|
+
{"updates": {"TERM": {"action": "add|update|remove", "weight": number, "falsePositiveRate": number, "reasoning": "string"}}}
|
|
282
|
+
|
|
283
|
+
## Current Keyword Store (${Object.keys(keywordStore.terms).length} terms):
|
|
284
|
+
${currentTerms}
|
|
285
|
+
|
|
286
|
+
## Recent User Messages (${recentMessages.length} messages):
|
|
287
|
+
${recentMessages.map((m, i) => `${i + 1}. "${m}"`).join('\n')}
|
|
288
|
+
|
|
289
|
+
## Rules:
|
|
290
|
+
- ADD: If a message contains frustration/empathy signals not in current terms
|
|
291
|
+
- UPDATE: If a term's weight should change (high hits → increase weight, low hits → decrease)
|
|
292
|
+
- REMOVE: If a term has 0 hits after many turns AND high false positive rate (>0.3)
|
|
293
|
+
- Keep reasoning concise (max 100 chars)
|
|
294
|
+
- Weight range: 0.1-0.9
|
|
295
|
+
- falsePositiveRate range: 0.05-0.5
|
|
296
|
+
`;
|
|
279
297
|
}
|
|
280
298
|
|
|
281
299
|
export async function handleBeforePromptBuild(
|
|
282
300
|
event: PluginHookBeforePromptBuildEvent,
|
|
283
301
|
ctx: PluginHookAgentContext & { api?: PromptHookApi }
|
|
284
302
|
): Promise<PluginHookBeforePromptBuildResult | void> {
|
|
285
|
-
const workspaceDir = ctx
|
|
286
|
-
|
|
303
|
+
const {workspaceDir} = ctx;
|
|
304
|
+
const logger = ctx.api?.logger;
|
|
305
|
+
logger?.info?.(`[PD:Prompt] handleBeforePromptBuild called: workspaceDir=${!!workspaceDir}, trigger=${ctx.trigger}, sessionId=${ctx.sessionId?.substring(0, 20)}`);
|
|
306
|
+
if (!workspaceDir) {
|
|
307
|
+
logger?.warn?.(`[PD:Prompt] workspaceDir is missing — skipping empathy processing`);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
287
310
|
|
|
288
311
|
const wctx = WorkspaceContext.fromHookContext(ctx);
|
|
289
312
|
const { trigger, sessionId, api } = ctx;
|
|
290
|
-
const logger = api?.logger;
|
|
291
313
|
if (sessionId) {
|
|
292
314
|
wctx.trajectory?.recordSession?.({ sessionId });
|
|
293
315
|
}
|
|
@@ -336,6 +358,7 @@ export async function handleBeforePromptBuild(
|
|
|
336
358
|
// appendSystemContext: Principles + Thinking OS + reflection_log + project_context (cacheable, WebUI-hidden)
|
|
337
359
|
// prependContext: Only short dynamic directives: evolutionDirective + heartbeat
|
|
338
360
|
|
|
361
|
+
// eslint-disable-next-line no-useless-assignment -- Reason: initial value unused due to immediate reassignment
|
|
339
362
|
let prependSystemContext = '';
|
|
340
363
|
let prependContext = '';
|
|
341
364
|
let appendSystemContext = '';
|
|
@@ -378,13 +401,53 @@ The empathy observer subagent handles pain detection independently.
|
|
|
378
401
|
`.trim();
|
|
379
402
|
|
|
380
403
|
// ─────────────────────────────────────────────────3. Empathy Observer Spawn
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
|
|
404
|
+
// event.prompt contains the full prompt text, which may include system/boot instructions
|
|
405
|
+
// The actual user message from Feishu is embedded in the prompt with various formats:
|
|
406
|
+
// Format 1: "Sender (untrusted metadata): ```json {...}``` user_message_text"
|
|
407
|
+
// Format 2: "You are running a boot check. Follow BOOT.md..." (boot check, skip empathy)
|
|
408
|
+
// Format 3: Clean user message text
|
|
409
|
+
let latestUserMessage = event.prompt || '';
|
|
410
|
+
|
|
411
|
+
// Skip boot check messages — these are system-generated, not real user messages.
|
|
412
|
+
// buildBootPrompt() in OpenClaw src/gateway/boot.ts always starts with:
|
|
413
|
+
// "You are running a boot check. Follow BOOT.md instructions exactly."
|
|
414
|
+
// This exact phrase will never appear in a real user message.
|
|
415
|
+
if (latestUserMessage.startsWith('You are running a boot check.') ||
|
|
416
|
+
latestUserMessage.includes('You are running a boot check. Follow BOOT.md')) {
|
|
417
|
+
latestUserMessage = '';
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Try to extract actual user message from Feishu wrapper formats
|
|
421
|
+
if (latestUserMessage.length > 50) {
|
|
422
|
+
// Format 1: "Sender (untrusted metadata): ```json {...}``` user_message_text"
|
|
423
|
+
const senderMatch = /Sender \(untrusted metadata\):[\s\S]*?```json[\s\S]*?```\s*/.exec(latestUserMessage);
|
|
424
|
+
if (senderMatch) {
|
|
425
|
+
const afterSender = latestUserMessage.slice(senderMatch.index + senderMatch[0].length).trim();
|
|
426
|
+
if (afterSender.length > 3) latestUserMessage = afterSender;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Format 2: "Conversation info (untrusted metadata): ```json {...}``` user_message_text"
|
|
430
|
+
if (latestUserMessage.length > 200 && latestUserMessage.includes('Conversation info')) {
|
|
431
|
+
const convInfoMatch = /Conversation info[\s\S]*?```json[\s\S]*?```\s*/.exec(latestUserMessage);
|
|
432
|
+
if (convInfoMatch) {
|
|
433
|
+
const afterConvInfo = latestUserMessage.slice(convInfoMatch.index + convInfoMatch[0].length).trim();
|
|
434
|
+
if (afterConvInfo.length > 3) latestUserMessage = afterConvInfo;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// #189: Detect empathy observer output to prevent recursive spawn.
|
|
440
|
+
// The empathy observer runs with parentSessionId (not :subagent:), so its output
|
|
441
|
+
// would be treated as a user message and re-trigger empathy evaluation.
|
|
442
|
+
// Match distinctive patterns from the empathy observer prompt/output.
|
|
443
|
+
const isEmpathyPrompt = /empathy\s*observer/i.test(latestUserMessage) &&
|
|
444
|
+
/damageDetected|severity|confidence/i.test(latestUserMessage);
|
|
445
|
+
const isAgentToAgent = latestUserMessage.includes('sourceSession=agent:') || sessionId?.includes(':subagent:') === true || isEmpathyPrompt;
|
|
384
446
|
|
|
385
447
|
const isUserInteraction = trigger === 'user' || trigger === 'api' || !trigger;
|
|
386
448
|
|
|
387
449
|
const empathyEnabled = wctx.config.get('empathy_engine.enabled') !== false;
|
|
450
|
+
logger?.info?.(`[PD:Empathy] Conditions: enabled=${empathyEnabled}, isUser=${isUserInteraction}, sessionId=${!!sessionId}, api=${!!api}, !agentToAgent=${!isAgentToAgent}, workspaceDir=${!!workspaceDir}, hasMessage=${!!latestUserMessage}`);
|
|
388
451
|
if (empathyEnabled && isUserInteraction && sessionId && api && !isAgentToAgent) {
|
|
389
452
|
prependContext = '### BEHAVIORAL_CONSTRAINTS\n' + empathySilenceConstraint + '\n\n' + prependContext;
|
|
390
453
|
|
|
@@ -393,6 +456,8 @@ The empathy observer subagent handles pain detection independently.
|
|
|
393
456
|
// for boundary cases and random discovery of new expressions.
|
|
394
457
|
if (workspaceDir && latestUserMessage) {
|
|
395
458
|
try {
|
|
459
|
+
const msgPreview = latestUserMessage.substring(0, 200).replace(/\n/g, ' ');
|
|
460
|
+
logger?.info?.(`[PD:Empathy] Processing user message: "${msgPreview}" (trigger=${trigger}, promptLen=${latestUserMessage.length})`);
|
|
396
461
|
const lang = (wctx.config.get('language') as 'zh' | 'en') || 'zh';
|
|
397
462
|
|
|
398
463
|
// Load keyword store once, cache in memory (Finding #7: avoid per-turn I/O)
|
|
@@ -431,7 +496,7 @@ The empathy observer subagent handles pain detection independently.
|
|
|
431
496
|
source: 'user_empathy',
|
|
432
497
|
});
|
|
433
498
|
|
|
434
|
-
logger?.info?.(`[PD:Empathy] MATCH: "${matchResult.matchedTerms.join(', ')}" → severity=${matchResult.severity}, score=${matchResult.score.toFixed(2)}, penalty=${penalty}, subagent=${shouldCallSubagent ? samplingReason : 'skipped(
|
|
499
|
+
logger?.info?.(`[PD:Empathy] MATCH: "${matchResult.matchedTerms.join(', ')}" → severity=${matchResult.severity}, score=${matchResult.score.toFixed(2)}, penalty=${penalty}, subagent=${shouldCallSubagent ? samplingReason : 'skipped(' + (matchResult.score >= 0.3 ? 'boundary_sampling' : 'random_discovery') + ')'}`);
|
|
435
500
|
} else {
|
|
436
501
|
// Log unmatched messages periodically for coverage analysis
|
|
437
502
|
if (turnCount > 0 && turnCount % 50 === 0) {
|
|
@@ -441,7 +506,12 @@ The empathy observer subagent handles pain detection independently.
|
|
|
441
506
|
}
|
|
442
507
|
|
|
443
508
|
// Trigger subagent for sampling cases (Finding #1: use shared manager to avoid leaks)
|
|
444
|
-
|
|
509
|
+
const runtimeSubagent =
|
|
510
|
+
isSubagentRuntimeAvailable(api?.runtime?.subagent)
|
|
511
|
+
? api.runtime.subagent
|
|
512
|
+
: undefined;
|
|
513
|
+
|
|
514
|
+
if (shouldCallSubagent && runtimeSubagent) {
|
|
445
515
|
logger?.info?.(`[PD:Empathy] SUBAGENT_SAMPLE: reason=${samplingReason}, score=${matchResult.score.toFixed(2)}, matched=[${matchResult.matchedTerms.join(',')}]`);
|
|
446
516
|
|
|
447
517
|
// EmpathyObserverWorkflowManager auto-finalizes via wait poll mechanism.
|
|
@@ -449,13 +519,18 @@ The empathy observer subagent handles pain detection independently.
|
|
|
449
519
|
const empathyManager = new EmpathyObserverWorkflowManager({
|
|
450
520
|
workspaceDir,
|
|
451
521
|
logger: api.logger ?? console,
|
|
452
|
-
|
|
522
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: runtimeSubagent has structurally compatible shape but differs from workflow manager's subagent type
|
|
523
|
+
subagent: runtimeSubagent as any,
|
|
453
524
|
});
|
|
454
525
|
empathyManager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
455
526
|
parentSessionId: sessionId,
|
|
456
527
|
workspaceDir,
|
|
457
528
|
taskInput: latestUserMessage,
|
|
458
|
-
}).catch((err) =>
|
|
529
|
+
}).catch((err) => {
|
|
530
|
+
if (!isExpectedSubagentError(err)) {
|
|
531
|
+
api.logger?.warn?.(`[PD:Empathy] subagent sample failed: ${String(err)}`);
|
|
532
|
+
}
|
|
533
|
+
});
|
|
459
534
|
}
|
|
460
535
|
|
|
461
536
|
// Helper: build summary string (Finding #2: avoid duplication)
|
|
@@ -469,7 +544,35 @@ The empathy observer subagent handles pain detection independently.
|
|
|
469
544
|
if (shouldTriggerOptimization(keywordStore, turnCount)) {
|
|
470
545
|
logger?.info?.(`[PD:Empathy] OPTIMIZATION_TRIGGER: turns=${turnCount}, last_optimized=${keywordStore.lastOptimizedAt}`);
|
|
471
546
|
logger?.info?.(`[PD:Empathy] STATS: ${buildSummary()}`);
|
|
472
|
-
|
|
547
|
+
|
|
548
|
+
// Start keyword optimization subagent to update weights and discover new terms
|
|
549
|
+
try {
|
|
550
|
+
const recentMessages = extractRecentMessages(event.messages, 10);
|
|
551
|
+
const optimizationPrompt = buildOptimizationPrompt(keywordStore, recentMessages);
|
|
552
|
+
|
|
553
|
+
logger?.info?.(`[PD:Empathy] Starting optimization subagent with ${recentMessages.length} recent messages`);
|
|
554
|
+
|
|
555
|
+
const empathyManager = new EmpathyObserverWorkflowManager({
|
|
556
|
+
workspaceDir,
|
|
557
|
+
logger: api.logger ?? console,
|
|
558
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: api.runtime.subagent has structurally compatible shape but differs from workflow manager's subagent type
|
|
559
|
+
subagent: api.runtime.subagent as any,
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
empathyManager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
563
|
+
parentSessionId: sessionId,
|
|
564
|
+
workspaceDir,
|
|
565
|
+
taskInput: { prompt: optimizationPrompt },
|
|
566
|
+
}).catch((err) => {
|
|
567
|
+
if (!isExpectedSubagentError(err)) {
|
|
568
|
+
api.logger?.warn?.(`[PD:Empathy] optimization subagent failed: ${String(err)}`);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
} catch (optErr) {
|
|
572
|
+
if (!isExpectedSubagentError(optErr)) {
|
|
573
|
+
logger?.warn?.(`[PD:Empathy] Failed to start optimization subagent: ${String(optErr)}`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
473
576
|
}
|
|
474
577
|
|
|
475
578
|
// Periodic summary (every 100 turns)
|
|
@@ -477,9 +580,13 @@ The empathy observer subagent handles pain detection independently.
|
|
|
477
580
|
logger?.info?.(`[PD:Empathy] ${buildSummary()}`);
|
|
478
581
|
}
|
|
479
582
|
|
|
480
|
-
// Save keyword store
|
|
481
|
-
|
|
583
|
+
// Save keyword store on every match to prevent data loss on restart.
|
|
584
|
+
// Previously used turnCount % 50 gate which caused hitCount loss because
|
|
585
|
+
// module-level state resets on plugin reload before reaching turn 50.
|
|
586
|
+
if (matchResult.matched) {
|
|
482
587
|
saveKeywordStore(wctx.stateDir, keywordStore);
|
|
588
|
+
const {totalHits} = keywordStore.stats;
|
|
589
|
+
logger?.info?.(`[PD:Empathy] Keyword store saved after match: terms=${matchResult.matchedTerms.join(',')}, totalHits=${totalHits}`);
|
|
483
590
|
}
|
|
484
591
|
} catch (e) {
|
|
485
592
|
logger?.warn?.(`[PD:Empathy] ERROR: ${String(e)}`);
|
|
@@ -521,6 +628,7 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
521
628
|
}
|
|
522
629
|
|
|
523
630
|
// ──── 6. Dynamic Attitude Matrix (based on GFI) ────
|
|
631
|
+
// eslint-disable-next-line no-useless-assignment -- Reason: initial value unused due to immediate reassignment
|
|
524
632
|
let attitudeDirective = '';
|
|
525
633
|
const currentGfi = session?.currentGfi || 0;
|
|
526
634
|
|
|
@@ -621,7 +729,7 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
621
729
|
if (currentFocus.trim()) {
|
|
622
730
|
try {
|
|
623
731
|
// 🚀 自动压缩门禁:检查文件大小,超过阈值自动压缩
|
|
624
|
-
const stateDir = wctx
|
|
732
|
+
const {stateDir} = wctx;
|
|
625
733
|
const compressResult = autoCompressFocus(focusPath, workspaceDir, stateDir);
|
|
626
734
|
if (compressResult.compressed) {
|
|
627
735
|
logger?.info?.(`[PD:Prompt] Auto-compressed CURRENT_FOCUS: ${compressResult.oldLines} → ${compressResult.newLines} lines. Milestones archived: ${compressResult.milestonesArchived}`);
|
|
@@ -732,12 +840,12 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
732
840
|
// Shadow evidence comes from real runtime hooks (subagent_spawning/subagent_ended).
|
|
733
841
|
if (!isMinimalMode && sessionId) {
|
|
734
842
|
try {
|
|
735
|
-
//
|
|
736
|
-
const latestUserText =
|
|
843
|
+
// Use the already extracted and cleaned user message
|
|
844
|
+
const latestUserText = latestUserMessage || '';
|
|
737
845
|
|
|
738
846
|
if (latestUserText && latestUserText.trim().length > 0) {
|
|
739
847
|
// Infer requestedTools and requestedFiles from message content
|
|
740
|
-
const toolPatterns:
|
|
848
|
+
const toolPatterns: { pattern: RegExp; tool: string }[] = [
|
|
741
849
|
{ pattern: /\b(edit|replace|write|modify|update|fix|patch|add|remove|delete|insert)\b/gi, tool: 'edit' },
|
|
742
850
|
{ pattern: /\b(read|cat|view|show|get|find|search|grep|look|inspect|examine|list|head|tail|diff)\b/gi, tool: 'read' },
|
|
743
851
|
{ pattern: /\b(run|execute|exec|bash|shell|command)\b/gi, tool: 'bash' },
|
|
@@ -745,9 +853,11 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
745
853
|
const filePattern = /\b([a-zA-Z]:\\?[^\s,]+\.[a-z]{2,10}|[./][^\s,]+\.[a-z]{2,10})\b/gi;
|
|
746
854
|
const toolMatches = toolPatterns.flatMap(({ pattern, tool }) => {
|
|
747
855
|
const matches: string[] = [];
|
|
748
|
-
|
|
856
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in while loop condition
|
|
857
|
+
let _m;
|
|
749
858
|
const r = new RegExp(pattern.source, pattern.flags);
|
|
750
|
-
|
|
859
|
+
/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: regex exec side effect used, match variable intentionally unused */
|
|
860
|
+
while ((_m = r.exec(latestUserText)) !== null) matches.push(tool);
|
|
751
861
|
return matches;
|
|
752
862
|
});
|
|
753
863
|
const fileMatches = latestUserText.match(filePattern) ?? [];
|
|
@@ -917,4 +1027,3 @@ ${attitudeDirective}
|
|
|
917
1027
|
appendSystemContext
|
|
918
1028
|
};
|
|
919
1029
|
}
|
|
920
|
-
|
package/src/hooks/subagent.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { PluginHookSubagentEndedEvent, PluginHookSubagentContext, PluginLogger, OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
2
|
import { buildPainFlag, writePainFlag } from '../core/pain.js';
|
|
4
3
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
5
4
|
// No longer needed — diagnostician runs via HEARTBEAT, not subagent
|
|
@@ -13,6 +12,7 @@ import type { WorkflowManager } from '../service/subagent-workflow/types.js';
|
|
|
13
12
|
* Factory to create the appropriate WorkflowManager by workflow_type string.
|
|
14
13
|
* Used by the subagent_ended hook to dispatch lifecycle recovery to the right manager.
|
|
15
14
|
*/
|
|
15
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Factory function requires workflowType, workspaceDir, logger, and subagent */
|
|
16
16
|
function createWorkflowManagerForType(
|
|
17
17
|
workflowType: string,
|
|
18
18
|
workspaceDir: string,
|
|
@@ -23,6 +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
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function -- debug logger no-op
|
|
26
27
|
debug: () => {},
|
|
27
28
|
} as unknown as PluginLogger;
|
|
28
29
|
|
|
@@ -84,7 +85,7 @@ function emitSubagentPainEvent(
|
|
|
84
85
|
function extractAgentIdFromSessionKey(sessionKey: string | undefined): string | undefined {
|
|
85
86
|
// sessionKey format: "agent:{agentId}:{type}:{uuid}" or "agent:{agentId}:{uuid}"
|
|
86
87
|
if (!sessionKey) return undefined;
|
|
87
|
-
const match =
|
|
88
|
+
const match = /^agent:([^:]+):/.exec(sessionKey);
|
|
88
89
|
return match ? match[1] : undefined;
|
|
89
90
|
}
|
|
90
91
|
|
|
@@ -104,7 +105,7 @@ export async function handleSubagentEnded(
|
|
|
104
105
|
ctx: SubagentEndedHookContext
|
|
105
106
|
): Promise<void> {
|
|
106
107
|
const { outcome, targetSessionKey } = event;
|
|
107
|
-
const workspaceDir = ctx
|
|
108
|
+
const {workspaceDir} = ctx;
|
|
108
109
|
|
|
109
110
|
if (!workspaceDir) return;
|
|
110
111
|
|
|
@@ -151,7 +152,7 @@ export async function handleSubagentEnded(
|
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
|
|
154
|
-
const config = wctx
|
|
155
|
+
const {config} = wctx;
|
|
155
156
|
|
|
156
157
|
// ── Outcome-based EP and Pain Signal handling ──
|
|
157
158
|
// OpenClaw v2026.3.23 fixes: timeout may be false positive (fast-finishing workers)
|
|
@@ -40,10 +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
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Checkpoint function requires event, config, sessionId, and logger */
|
|
43
44
|
export function checkThinkingCheckpoint(
|
|
44
45
|
event: PluginHookBeforeToolCallEvent,
|
|
45
46
|
config: ThinkingCheckpointConfig,
|
|
46
47
|
sessionId: string | undefined,
|
|
48
|
+
/* eslint-disable no-unused-vars -- Reason: callback parameter in type annotation */
|
|
47
49
|
logger?: { info?: (message: string) => void }
|
|
48
50
|
): PluginHookBeforeToolCallResult | undefined {
|
|
49
51
|
const enabled = config.enabled ?? false;
|
|
@@ -63,7 +63,7 @@ function scrubSensitive(obj: unknown, depth = 0): unknown {
|
|
|
63
63
|
* 异步写入队列 - 确保有序、非阻塞写入
|
|
64
64
|
*/
|
|
65
65
|
class AsyncWriteQueue {
|
|
66
|
-
private queue:
|
|
66
|
+
private readonly queue: (() => Promise<void>)[] = [];
|
|
67
67
|
private processing = false;
|
|
68
68
|
|
|
69
69
|
async enqueue(task: () => Promise<void>): Promise<void> {
|
|
@@ -81,9 +81,14 @@ class AsyncWriteQueue {
|
|
|
81
81
|
|
|
82
82
|
this.processing = true;
|
|
83
83
|
const task = this.queue.shift();
|
|
84
|
-
|
|
84
|
+
|
|
85
|
+
if (!task) {
|
|
86
|
+
this.processing = false;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
85
90
|
try {
|
|
86
|
-
await task
|
|
91
|
+
await task();
|
|
87
92
|
} catch {
|
|
88
93
|
// Silently fail - trajectory collection should not block main functionality
|
|
89
94
|
}
|
|
@@ -151,7 +156,7 @@ export function handleAfterToolCall(
|
|
|
151
156
|
event: PluginHookAfterToolCallEvent,
|
|
152
157
|
ctx: PluginHookToolContext & { workspaceDir?: string }
|
|
153
158
|
): void {
|
|
154
|
-
const workspaceDir = ctx
|
|
159
|
+
const {workspaceDir} = ctx;
|
|
155
160
|
if (!workspaceDir) return;
|
|
156
161
|
|
|
157
162
|
// 递归脱敏处理所有字段
|
|
@@ -186,7 +191,7 @@ export function handleLlmOutput(
|
|
|
186
191
|
event: PluginHookLlmOutputEvent,
|
|
187
192
|
ctx: PluginHookAgentContext & { workspaceDir?: string }
|
|
188
193
|
): void {
|
|
189
|
-
const workspaceDir = ctx
|
|
194
|
+
const {workspaceDir} = ctx;
|
|
190
195
|
if (!workspaceDir) return;
|
|
191
196
|
|
|
192
197
|
const totalTextLength = event.assistantTexts?.reduce((sum, text) => sum + (text?.length || 0), 0) || 0;
|
|
@@ -211,7 +216,7 @@ export function handleBeforeMessageWrite(
|
|
|
211
216
|
event: PluginHookBeforeMessageWriteEvent,
|
|
212
217
|
ctx: PluginHookAgentContext & { workspaceDir?: string }
|
|
213
218
|
): void {
|
|
214
|
-
const workspaceDir = ctx
|
|
219
|
+
const {workspaceDir} = ctx;
|
|
215
220
|
if (!workspaceDir) return;
|
|
216
221
|
|
|
217
222
|
const msg = event.message;
|
|
@@ -223,11 +228,13 @@ export function handleBeforeMessageWrite(
|
|
|
223
228
|
// 提取文本内容
|
|
224
229
|
let content = '';
|
|
225
230
|
if (typeof msg.content === 'string') {
|
|
231
|
+
/* eslint-disable @typescript-eslint/prefer-destructuring */
|
|
232
|
+
// Reason: msg.content is string | ContentPart[]; destructuring would require renaming in the else branch
|
|
226
233
|
content = msg.content;
|
|
227
234
|
} else if (Array.isArray(msg.content)) {
|
|
228
235
|
content = msg.content
|
|
229
|
-
.filter((part:
|
|
230
|
-
.map((part:
|
|
236
|
+
.filter((part: unknown) => part && typeof part === 'object' && (part as { type?: string }).type === 'text')
|
|
237
|
+
.map((part: unknown) => (part as { text: string }).text)
|
|
231
238
|
.join('\n');
|
|
232
239
|
}
|
|
233
240
|
|
|
@@ -249,10 +256,6 @@ export function handleBeforeMessageWrite(
|
|
|
249
256
|
* 脱敏处理:移除敏感参数(保留旧函数签名以兼容)
|
|
250
257
|
* @deprecated 使用 scrubSensitive 替代
|
|
251
258
|
*/
|
|
252
|
-
function sanitizeParams(params: Record<string, any>): Record<string, any> {
|
|
253
|
-
return scrubSensitive(params) as Record<string, any>;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
259
|
/**
|
|
257
260
|
* 轨迹汇总统计(供 cron 任务调用)
|
|
258
261
|
*/
|