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
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import type { WorkflowRow, WorkflowEventRow, WorkflowState
|
|
4
|
+
import type { WorkflowRow, WorkflowEventRow, WorkflowState } from './types.js';
|
|
5
5
|
import type { DreamerOutput, PhilosopherOutput } from '../../core/nocturnal-trinity.js';
|
|
6
6
|
|
|
7
|
-
const SCHEMA_VERSION =
|
|
7
|
+
const SCHEMA_VERSION = 2;
|
|
8
8
|
|
|
9
9
|
const DEFAULT_BUSY_TIMEOUT_MS = 5000;
|
|
10
10
|
|
|
@@ -42,7 +42,7 @@ export class WorkflowStore {
|
|
|
42
42
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
43
43
|
version INTEGER NOT NULL
|
|
44
44
|
);
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
CREATE TABLE IF NOT EXISTS subagent_workflows (
|
|
47
47
|
workflow_id TEXT PRIMARY KEY,
|
|
48
48
|
workflow_type TEXT NOT NULL,
|
|
@@ -55,6 +55,7 @@ export class WorkflowStore {
|
|
|
55
55
|
created_at INTEGER NOT NULL,
|
|
56
56
|
updated_at INTEGER NOT NULL,
|
|
57
57
|
last_observed_at INTEGER,
|
|
58
|
+
duration_ms INTEGER,
|
|
58
59
|
metadata_json TEXT NOT NULL DEFAULT '{}'
|
|
59
60
|
);
|
|
60
61
|
|
|
@@ -89,15 +90,29 @@ export class WorkflowStore {
|
|
|
89
90
|
`);
|
|
90
91
|
|
|
91
92
|
const row = this.db.prepare('SELECT version FROM schema_version LIMIT 1').get() as { version?: number } | undefined;
|
|
92
|
-
|
|
93
|
+
const currentVersion = row?.version ?? 0;
|
|
94
|
+
if (currentVersion === 0) {
|
|
93
95
|
this.db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(SCHEMA_VERSION);
|
|
94
|
-
} else if (
|
|
96
|
+
} else if (currentVersion < SCHEMA_VERSION) {
|
|
97
|
+
// Run migrations for existing databases
|
|
98
|
+
this.runMigrations(currentVersion, SCHEMA_VERSION);
|
|
95
99
|
this.db.prepare('UPDATE schema_version SET version = ?').run(SCHEMA_VERSION);
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
|
-
|
|
102
|
+
|
|
103
|
+
private runMigrations(fromVersion: number, toVersion: number): void {
|
|
104
|
+
if (fromVersion < 2 && toVersion >= 2) {
|
|
105
|
+
// v1 → v2: Add duration_ms column for adaptive timeout tracking
|
|
106
|
+
try {
|
|
107
|
+
this.db.exec('ALTER TABLE subagent_workflows ADD COLUMN duration_ms INTEGER');
|
|
108
|
+
console.info(`[PD:WorkflowStore] Schema migration v${fromVersion} → v${toVersion}: added duration_ms column`);
|
|
109
|
+
} catch {
|
|
110
|
+
// Column may already exist if migration was partially applied
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
99
115
|
createWorkflow(row: Omit<WorkflowRow, 'cleanup_state'>): void {
|
|
100
|
-
const now = Date.now();
|
|
101
116
|
this.db.prepare(`
|
|
102
117
|
INSERT INTO subagent_workflows (
|
|
103
118
|
workflow_id, workflow_type, transport, parent_session_id, child_session_key,
|
|
@@ -201,7 +216,23 @@ export class WorkflowStore {
|
|
|
201
216
|
ORDER BY last_observed_at ASC
|
|
202
217
|
`).all(cutoff) as WorkflowRow[];
|
|
203
218
|
}
|
|
219
|
+
|
|
220
|
+
/** List all workflows, optionally filtered by state. */
|
|
221
|
+
listWorkflows(state?: string): WorkflowRow[] {
|
|
222
|
+
if (state) {
|
|
223
|
+
return this.db.prepare(`
|
|
224
|
+
SELECT * FROM subagent_workflows
|
|
225
|
+
WHERE state = ?
|
|
226
|
+
ORDER BY created_at DESC
|
|
227
|
+
`).all(state) as WorkflowRow[];
|
|
228
|
+
}
|
|
229
|
+
return this.db.prepare(`
|
|
230
|
+
SELECT * FROM subagent_workflows
|
|
231
|
+
ORDER BY created_at DESC
|
|
232
|
+
`).all() as WorkflowRow[];
|
|
233
|
+
}
|
|
204
234
|
|
|
235
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Store interface requires full event context (workflowId, fromState, toState, reason, payload) */
|
|
205
236
|
recordEvent(
|
|
206
237
|
workflowId: string,
|
|
207
238
|
eventType: string,
|
|
@@ -237,6 +268,7 @@ export class WorkflowStore {
|
|
|
237
268
|
* idempotencyKey must be unique per (workflow_id, stage). If a row with the
|
|
238
269
|
* same idempotency_key already exists, this is a no-op (idempotent).
|
|
239
270
|
*/
|
|
271
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Store interface requires workflowId, stage, output, and idempotencyKey */
|
|
240
272
|
recordStageOutput(
|
|
241
273
|
workflowId: string,
|
|
242
274
|
stage: 'dreamer' | 'philosopher',
|
|
@@ -262,24 +294,24 @@ export class WorkflowStore {
|
|
|
262
294
|
* Get all stage outputs for a workflow (NOC-13 crash recovery).
|
|
263
295
|
* Returns outputs ordered by created_at ascending.
|
|
264
296
|
*/
|
|
265
|
-
getStageOutputs(workflowId: string):
|
|
297
|
+
getStageOutputs(workflowId: string): {
|
|
266
298
|
stage: string;
|
|
267
299
|
output: DreamerOutput | PhilosopherOutput;
|
|
268
300
|
idempotencyKey: string;
|
|
269
301
|
createdAt: number;
|
|
270
|
-
}
|
|
302
|
+
}[] {
|
|
271
303
|
const rows = this.db.prepare(`
|
|
272
304
|
SELECT workflow_id, stage, output_json, idempotency_key, created_at
|
|
273
305
|
FROM subagent_workflow_stage_outputs
|
|
274
306
|
WHERE workflow_id = ?
|
|
275
307
|
ORDER BY created_at ASC
|
|
276
|
-
`).all(workflowId) as
|
|
308
|
+
`).all(workflowId) as {
|
|
277
309
|
workflow_id: string;
|
|
278
310
|
stage: string;
|
|
279
311
|
output_json: string;
|
|
280
312
|
idempotency_key: string;
|
|
281
313
|
created_at: number;
|
|
282
|
-
}
|
|
314
|
+
}[];
|
|
283
315
|
|
|
284
316
|
return rows.map(row => ({
|
|
285
317
|
workflowId: row.workflow_id,
|
|
@@ -325,4 +357,32 @@ export class WorkflowStore {
|
|
|
325
357
|
deleteWorkflow(workflowId: string): void {
|
|
326
358
|
this.db.prepare('DELETE FROM subagent_workflows WHERE workflow_id = ?').run(workflowId);
|
|
327
359
|
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Record the actual completion duration for a workflow.
|
|
363
|
+
* Used by adaptive timeout learning.
|
|
364
|
+
*/
|
|
365
|
+
recordDuration(workflowId: string, durationMs: number): void {
|
|
366
|
+
this.db.prepare(`
|
|
367
|
+
UPDATE subagent_workflows SET duration_ms = ?, updated_at = ? WHERE workflow_id = ?
|
|
368
|
+
`).run(durationMs, Date.now(), workflowId);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Get completion durations for a specific workflow type, ordered by most recent first.
|
|
373
|
+
* Returns an array of duration_ms values for adaptive timeout calculation.
|
|
374
|
+
*/
|
|
375
|
+
getCompletionDurations(workflowType: string, limit = 50): number[] {
|
|
376
|
+
const rows = this.db.prepare(`
|
|
377
|
+
SELECT duration_ms FROM subagent_workflows
|
|
378
|
+
WHERE workflow_type = ?
|
|
379
|
+
AND state = 'completed'
|
|
380
|
+
AND duration_ms IS NOT NULL
|
|
381
|
+
AND duration_ms > 0
|
|
382
|
+
ORDER BY created_at DESC
|
|
383
|
+
LIMIT ?
|
|
384
|
+
`).all(workflowType, limit) as { duration_ms: number }[];
|
|
385
|
+
|
|
386
|
+
return rows.map(r => r.duration_ms);
|
|
387
|
+
}
|
|
328
388
|
}
|
|
@@ -6,7 +6,8 @@ export const TrajectoryService: OpenClawPluginService = {
|
|
|
6
6
|
id: 'principles-disciple-trajectory',
|
|
7
7
|
start(ctx: OpenClawPluginServiceContext): void {
|
|
8
8
|
if (!ctx.workspaceDir) return;
|
|
9
|
-
|
|
9
|
+
// Trigger lazy initialization of trajectory registry
|
|
10
|
+
void WorkspaceContext.fromHookContext(ctx).trajectory;
|
|
10
11
|
},
|
|
11
12
|
stop(ctx: OpenClawPluginServiceContext): void {
|
|
12
13
|
if (!ctx.workspaceDir) return;
|
|
@@ -26,7 +26,7 @@ export function buildCritiquePromptV2(
|
|
|
26
26
|
api?: OpenClawPluginApi;
|
|
27
27
|
}
|
|
28
28
|
): string {
|
|
29
|
-
const { context, depth = 2,
|
|
29
|
+
const { context, depth = 2, workspaceDir, api } = params;
|
|
30
30
|
|
|
31
31
|
// 1. 确定工作区目录 (优先级:显式传入 > api.config > official API > api.resolvePath)
|
|
32
32
|
const effectiveWorkspaceDir = workspaceDir
|
|
@@ -1,94 +1,15 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
2
2
|
import { Type } from '@sinclair/typebox';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
4
|
import { EventLogService } from '../core/event-log.js';
|
|
6
|
-
import { buildCritiquePromptV2 } from './critique-prompt.js';
|
|
7
5
|
import { resolvePdPath } from '../core/paths.js';
|
|
8
6
|
import { resolveWorkspaceDirFromApi } from '../core/path-resolver.js';
|
|
9
|
-
import { reflectionLogRetentionDays } from '../types.js';
|
|
10
7
|
import {
|
|
11
8
|
DeepReflectWorkflowManager,
|
|
12
9
|
deepReflectWorkflowSpec,
|
|
13
10
|
type DeepReflectTaskInput,
|
|
14
11
|
} from '../service/subagent-workflow/index.js';
|
|
15
12
|
|
|
16
|
-
function writeToReflectionLog(
|
|
17
|
-
workspaceDir: string,
|
|
18
|
-
context: string,
|
|
19
|
-
insights: string,
|
|
20
|
-
modelId?: string,
|
|
21
|
-
depth?: number
|
|
22
|
-
): void {
|
|
23
|
-
const reflectionLogPath = resolvePdPath(workspaceDir, 'REFLECTION_LOG');
|
|
24
|
-
const memoryDir = path.dirname(reflectionLogPath);
|
|
25
|
-
if (!fs.existsSync(memoryDir)) {
|
|
26
|
-
fs.mkdirSync(memoryDir, { recursive: true });
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const timestamp = new Date().toISOString();
|
|
30
|
-
const entry = `
|
|
31
|
-
---
|
|
32
|
-
## Reflection at ${timestamp}
|
|
33
|
-
**Model**: ${modelId || 'auto-select'}
|
|
34
|
-
**Depth**: ${depth || 2}
|
|
35
|
-
|
|
36
|
-
### Context
|
|
37
|
-
${context.substring(0, 500)}${context.length > 500 ? '...' : ''}
|
|
38
|
-
|
|
39
|
-
### Insights
|
|
40
|
-
${insights}
|
|
41
|
-
|
|
42
|
-
`;
|
|
43
|
-
|
|
44
|
-
const header = `# Reflection Log\n\n> Auto-generated by Deep Reflection Tool\n> Retention: ${reflectionLogRetentionDays} days\n`;
|
|
45
|
-
|
|
46
|
-
let existingContent = '';
|
|
47
|
-
if (fs.existsSync(reflectionLogPath)) {
|
|
48
|
-
existingContent = fs.readFileSync(reflectionLogPath, 'utf8');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const newContent = header + entry + existingContent.replace(header, '');
|
|
52
|
-
|
|
53
|
-
const tempPath = `${reflectionLogPath}.tmp`;
|
|
54
|
-
fs.writeFileSync(tempPath, newContent, 'utf8');
|
|
55
|
-
fs.renameSync(tempPath, reflectionLogPath);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function cleanupReflectionLog(workspaceDir: string): void {
|
|
59
|
-
const reflectionLogPath = resolvePdPath(workspaceDir, 'REFLECTION_LOG');
|
|
60
|
-
|
|
61
|
-
if (!fs.existsSync(reflectionLogPath)) return;
|
|
62
|
-
|
|
63
|
-
const content = fs.readFileSync(reflectionLogPath, 'utf8');
|
|
64
|
-
const cutoffDate = new Date();
|
|
65
|
-
cutoffDate.setDate(cutoffDate.getDate() - reflectionLogRetentionDays);
|
|
66
|
-
|
|
67
|
-
const entryPattern = /---\n## Reflection at (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})/g;
|
|
68
|
-
const parts = content.split(entryPattern);
|
|
69
|
-
|
|
70
|
-
const header = parts[0];
|
|
71
|
-
const validEntries: string[] = [];
|
|
72
|
-
|
|
73
|
-
for (let i = 1; i < parts.length; i += 2) {
|
|
74
|
-
const timestamp = parts[i];
|
|
75
|
-
const entryContent = parts[i + 1] || '';
|
|
76
|
-
|
|
77
|
-
const entryDate = new Date(timestamp);
|
|
78
|
-
if (entryDate >= cutoffDate) {
|
|
79
|
-
validEntries.push(`---\n## Reflection at ${timestamp}${entryContent}`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (validEntries.length < (parts.length - 1) / 2) {
|
|
84
|
-
const newContent = header + validEntries.join('');
|
|
85
|
-
|
|
86
|
-
const tempPath = `${reflectionLogPath}.tmp`;
|
|
87
|
-
fs.writeFileSync(tempPath, newContent, 'utf8');
|
|
88
|
-
fs.renameSync(tempPath, reflectionLogPath);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
13
|
interface DeepReflectionConfig {
|
|
93
14
|
enabled: boolean;
|
|
94
15
|
mode: 'auto' | 'forced' | 'disabled';
|
|
@@ -176,111 +97,164 @@ export function createDeepReflectTool(api: OpenClawPluginApi) {
|
|
|
176
97
|
async execute(
|
|
177
98
|
_toolCallId: string,
|
|
178
99
|
rawParams: Record<string, unknown>
|
|
179
|
-
): Promise<{ content:
|
|
100
|
+
): Promise<{ content: { type: string; text: string }[] }> {
|
|
180
101
|
const context = readStringParam(rawParams, 'context') || '';
|
|
181
102
|
const depth = readNumberParam(rawParams, 'depth') ?? 2;
|
|
182
103
|
const model_id = readStringParam(rawParams, 'model_id');
|
|
183
104
|
|
|
184
105
|
if (!context) {
|
|
185
|
-
return {
|
|
186
|
-
content: [{
|
|
187
|
-
type: 'text',
|
|
188
|
-
text: '❌ 错误: 必须提供反思上下文 (context)。'
|
|
189
|
-
}]
|
|
190
|
-
};
|
|
106
|
+
return { content: [{ type: 'text', text: '❌ 错误: 必须提供反思上下文 (context)。' }] };
|
|
191
107
|
}
|
|
192
108
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|| resolveWorkspaceDirFromApi(api)
|
|
196
|
-
|| api.resolvePath?.('.');
|
|
197
|
-
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
110
|
+
const effectiveWorkspaceDir = resolveReflectionWorkspace(api);
|
|
198
111
|
if (!effectiveWorkspaceDir) {
|
|
199
|
-
return {
|
|
200
|
-
content: [{
|
|
201
|
-
type: 'text',
|
|
202
|
-
text: '❌ 反思执行失败: Workspace directory is required for deep reflection。请检查 API 配置或网络连接。'
|
|
203
|
-
}]
|
|
204
|
-
};
|
|
112
|
+
return { content: [{ type: 'text', text: '❌ 反思执行失败: Workspace directory is required for deep reflection。请检查 API 配置或网络连接。' }] };
|
|
205
113
|
}
|
|
206
114
|
|
|
207
115
|
const config = loadConfig(effectiveWorkspaceDir, api);
|
|
208
116
|
if (config.mode === 'disabled' || !config.enabled) {
|
|
209
|
-
return {
|
|
210
|
-
content: [{
|
|
211
|
-
type: 'text',
|
|
212
|
-
text: '⏭️ Deep Reflection 已禁用。'
|
|
213
|
-
}]
|
|
214
|
-
};
|
|
117
|
+
return { content: [{ type: 'text', text: '⏭️ Deep Reflection 已禁用。' }] };
|
|
215
118
|
}
|
|
216
119
|
|
|
217
120
|
if (model_id) {
|
|
218
121
|
safeLog(api, 'warn', `[DeepReflect] The 'model_id' parameter is deprecated. The agent will now auto-select models based on the context index.`);
|
|
219
122
|
}
|
|
220
123
|
|
|
221
|
-
const stateDir = resolvePdPath(effectiveWorkspaceDir, 'STATE_DIR');
|
|
222
|
-
const eventLog = EventLogService.get(stateDir, api.logger);
|
|
223
|
-
|
|
224
124
|
try {
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
126
|
+
return await executeReflectionWorkflow(effectiveWorkspaceDir, config, context, depth, model_id, api);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
129
|
+
return handleReflectionError(err, context, depth, model_id, effectiveWorkspaceDir, api);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Resolve workspace directory for deep reflection tool.
|
|
137
|
+
*/
|
|
138
|
+
function resolveReflectionWorkspace(api: OpenClawPluginApi): string | undefined {
|
|
139
|
+
return (api.config?.workspaceDir as string)
|
|
140
|
+
|| resolveWorkspaceDirFromApi(api)
|
|
141
|
+
|| api.resolvePath?.('.');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Execute the deep reflection workflow: start, poll, collect results.
|
|
146
|
+
*/
|
|
147
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe reflection workflow */
|
|
148
|
+
async function executeReflectionWorkflow(
|
|
149
|
+
effectiveWorkspaceDir: string,
|
|
150
|
+
config: DeepReflectionConfig,
|
|
151
|
+
context: string,
|
|
152
|
+
depth: number,
|
|
153
|
+
model_id: string | undefined,
|
|
154
|
+
api: OpenClawPluginApi,
|
|
155
|
+
): Promise<{ content: { type: string; text: string }[] }> {
|
|
156
|
+
const stateDir = resolvePdPath(effectiveWorkspaceDir, 'STATE_DIR');
|
|
157
|
+
const eventLog = EventLogService.get(stateDir, api.logger);
|
|
158
|
+
const parentSessionId = effectiveWorkspaceDir.replace(/[^a-zA-Z0-9_-]/g, '_').substring(0, 64);
|
|
159
|
+
|
|
160
|
+
const manager = new DeepReflectWorkflowManager({
|
|
161
|
+
workspaceDir: effectiveWorkspaceDir,
|
|
162
|
+
logger: api.logger,
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: api.runtime.subagent has structurally compatible shape but differs from PluginRuntimeSubagent due to optional provider/model fields
|
|
164
|
+
subagent: api.runtime.subagent as any,
|
|
165
|
+
agentSession: api.runtime.agent?.session,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const taskInput: DeepReflectTaskInput = { context, depth, model_id };
|
|
170
|
+
const handle = await manager.startWorkflow(deepReflectWorkflowSpec, {
|
|
171
|
+
parentSessionId,
|
|
172
|
+
workspaceDir: effectiveWorkspaceDir,
|
|
173
|
+
taskInput,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const startTime = Date.now();
|
|
177
|
+
const timeoutMs = config.timeout_ms ?? 60000;
|
|
178
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
179
|
+
return await pollReflectionCompletion(manager, handle, timeoutMs, startTime, eventLog, effectiveWorkspaceDir, context, model_id, depth);
|
|
180
|
+
} finally {
|
|
181
|
+
manager.dispose();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Poll the reflection workflow until completion, timeout, or error.
|
|
187
|
+
*/
|
|
188
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe polling */
|
|
189
|
+
async function pollReflectionCompletion(
|
|
190
|
+
manager: DeepReflectWorkflowManager,
|
|
191
|
+
handle: { workflowId: string; childSessionKey: string },
|
|
192
|
+
timeoutMs: number,
|
|
193
|
+
startTime: number,
|
|
194
|
+
eventLog: ReturnType<typeof EventLogService.get>,
|
|
195
|
+
workspaceDir: string,
|
|
196
|
+
context: string,
|
|
197
|
+
model_id: string | undefined,
|
|
198
|
+
depth: number,
|
|
199
|
+
): Promise<{ content: { type: string; text: string }[] }> {
|
|
200
|
+
const pollInterval = 500;
|
|
201
|
+
|
|
202
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
203
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
204
|
+
const workflowState = manager.getWorkflowState(handle.workflowId);
|
|
205
|
+
if (!workflowState) break;
|
|
206
|
+
|
|
207
|
+
if (workflowState === 'completed') {
|
|
208
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
209
|
+
return formatReflectionSuccess(handle, context, depth, model_id, startTime, eventLog, workspaceDir);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (workflowState === 'terminal_error' || workflowState === 'expired') {
|
|
213
|
+
throw new Error(`Deep-reflect workflow failed: ${workflowState}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return { content: [{ type: 'text', text: '⚠️ 反思任务执行超时。你可以尝试减少上下文长度或增加深度。' }] };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Format the success response from a completed reflection.
|
|
222
|
+
*/
|
|
223
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe formatting */
|
|
224
|
+
function formatReflectionSuccess(
|
|
225
|
+
handle: { childSessionKey: string },
|
|
226
|
+
context: string,
|
|
227
|
+
depth: number,
|
|
228
|
+
model_id: string | undefined,
|
|
229
|
+
startTime: number,
|
|
230
|
+
eventLog: ReturnType<typeof EventLogService.get>,
|
|
231
|
+
workspaceDir: string,
|
|
232
|
+
): { content: { type: string; text: string }[] } {
|
|
233
|
+
const reflectionLogPath = resolvePdPath(workspaceDir, 'REFLECTION_LOG');
|
|
234
|
+
let insights = '';
|
|
235
|
+
if (fs.existsSync(reflectionLogPath)) {
|
|
236
|
+
const content = fs.readFileSync(reflectionLogPath, 'utf8');
|
|
237
|
+
const match = /### Insights\n([\s\S]*?)(?=---|$)/.exec(content);
|
|
238
|
+
if (match) insights = match[1].trim();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (eventLog) {
|
|
242
|
+
eventLog.recordDeepReflection(handle.childSessionKey, {
|
|
243
|
+
modelId: model_id || 'auto-select',
|
|
244
|
+
modelSelectionMode: model_id ? 'manual' : 'auto',
|
|
245
|
+
depth,
|
|
246
|
+
contextPreview: context.substring(0, 200),
|
|
247
|
+
resultPreview: insights.substring(0, 300),
|
|
248
|
+
durationMs: Date.now() - startTime,
|
|
249
|
+
passed: true,
|
|
250
|
+
timeout: false,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
content: [{
|
|
256
|
+
type: 'text',
|
|
257
|
+
text: `
|
|
284
258
|
# 💎 Deep Reflection Insights
|
|
285
259
|
---
|
|
286
260
|
**Selected Model(s)**: ${model_id || 'auto-select'}
|
|
@@ -291,53 +265,45 @@ ${insights || '反思完成,详见 REFLECTION_LOG。'}
|
|
|
291
265
|
|
|
292
266
|
---
|
|
293
267
|
*Generated by Principles Disciple Meta-Cognitive Engine*
|
|
294
|
-
`.trim()
|
|
295
|
-
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (workflow.state === 'terminal_error' || workflow.state === 'expired') {
|
|
300
|
-
manager.dispose();
|
|
301
|
-
throw new Error(`Deep-reflect workflow failed: ${workflow.state}`);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
manager.dispose();
|
|
306
|
-
return {
|
|
307
|
-
content: [{
|
|
308
|
-
type: 'text',
|
|
309
|
-
text: '⚠️ 反思任务执行超时。你可以尝试减少上下文长度或增加深度。'
|
|
310
|
-
}]
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
} catch (err) {
|
|
314
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
315
|
-
safeLog(api, 'error', `[DeepReflect] Reflection failed: ${errorMsg}`);
|
|
316
|
-
|
|
317
|
-
if (eventLog) {
|
|
318
|
-
eventLog.recordDeepReflection('deep-reflect-error', {
|
|
319
|
-
modelId: model_id || 'auto-select',
|
|
320
|
-
modelSelectionMode: model_id ? 'manual' : 'auto',
|
|
321
|
-
depth,
|
|
322
|
-
contextPreview: context.substring(0, 200),
|
|
323
|
-
durationMs: 0,
|
|
324
|
-
passed: false,
|
|
325
|
-
timeout: errorMsg.toLowerCase().includes('timeout'),
|
|
326
|
-
error: errorMsg
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return {
|
|
331
|
-
content: [{
|
|
332
|
-
type: 'text',
|
|
333
|
-
text: `❌ 反思执行失败: ${errorMsg}。请检查 API 配置或网络连接。`
|
|
334
|
-
}]
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
}
|
|
268
|
+
`.trim(),
|
|
269
|
+
}],
|
|
338
270
|
};
|
|
339
271
|
}
|
|
340
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Handle reflection errors and format error response.
|
|
275
|
+
*/
|
|
276
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe error handling */
|
|
277
|
+
function handleReflectionError(
|
|
278
|
+
err: unknown,
|
|
279
|
+
context: string,
|
|
280
|
+
depth: number,
|
|
281
|
+
model_id: string | undefined,
|
|
282
|
+
workspaceDir: string,
|
|
283
|
+
api: OpenClawPluginApi,
|
|
284
|
+
): { content: { type: string; text: string }[] } {
|
|
285
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
286
|
+
safeLog(api, 'error', `[DeepReflect] Reflection failed: ${errorMsg}`);
|
|
287
|
+
|
|
288
|
+
const stateDir = resolvePdPath(workspaceDir, 'STATE_DIR');
|
|
289
|
+
const eventLog = EventLogService.get(stateDir, api.logger);
|
|
290
|
+
|
|
291
|
+
if (eventLog) {
|
|
292
|
+
eventLog.recordDeepReflection('deep-reflect-error', {
|
|
293
|
+
modelId: model_id || 'auto-select',
|
|
294
|
+
modelSelectionMode: model_id ? 'manual' : 'auto',
|
|
295
|
+
depth,
|
|
296
|
+
contextPreview: context.substring(0, 200),
|
|
297
|
+
durationMs: 0,
|
|
298
|
+
passed: false,
|
|
299
|
+
timeout: errorMsg.toLowerCase().includes('timeout'),
|
|
300
|
+
error: errorMsg,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return { content: [{ type: 'text', text: `❌ 反思执行失败: ${errorMsg}。请检查 API 配置或网络连接。` }] };
|
|
305
|
+
}
|
|
306
|
+
|
|
341
307
|
export const deepReflectTool = {
|
|
342
308
|
name: 'deep_reflect',
|
|
343
309
|
description: '执行深层次的元认知反思,分析当前任务的潜在风险、逻辑漏洞或架构改进点。',
|
package/src/tools/model-index.ts
CHANGED
|
@@ -33,7 +33,7 @@ const MAX_INDEX_SIZE = 50 * 1024; // 50KB
|
|
|
33
33
|
*/
|
|
34
34
|
function loadCustomConfig(wctx: WorkspaceContext): { modelsDir?: string } | undefined {
|
|
35
35
|
try {
|
|
36
|
-
const config = wctx
|
|
36
|
+
const {config} = wctx;
|
|
37
37
|
const modelsDir = config.get('deep_reflection.modelsDir');
|
|
38
38
|
if (typeof modelsDir === 'string' && modelsDir.trim()) {
|
|
39
39
|
return { modelsDir: modelsDir.trim() };
|
|
@@ -61,6 +61,7 @@ export function loadModelIndex(
|
|
|
61
61
|
const wctx = WorkspaceContext.fromHookContext({ workspaceDir });
|
|
62
62
|
const customConfig = loadCustomConfig(wctx);
|
|
63
63
|
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in both if/else branches
|
|
64
65
|
let modelsDir: string;
|
|
65
66
|
if (customConfig?.modelsDir) {
|
|
66
67
|
modelsDir = path.isAbsolute(customConfig.modelsDir)
|
package/src/types/event-types.ts
CHANGED
|
@@ -245,11 +245,11 @@ export interface EmpathyEventStats {
|
|
|
245
245
|
low: number; // confidence < 0.5
|
|
246
246
|
};
|
|
247
247
|
/** Daily trend (last 7 days) */
|
|
248
|
-
dailyTrend:
|
|
248
|
+
dailyTrend: {
|
|
249
249
|
date: string;
|
|
250
250
|
count: number;
|
|
251
251
|
score: number;
|
|
252
|
-
}
|
|
252
|
+
}[];
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
export interface GfiStats {
|