principles-disciple 1.7.5 → 1.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/evolution-status.js +32 -44
- package/dist/config/defaults/runtime.d.ts +40 -0
- package/dist/config/defaults/runtime.js +44 -0
- package/dist/config/errors.d.ts +84 -0
- package/dist/config/errors.js +94 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.js +7 -0
- package/dist/constants/diagnostician.d.ts +0 -4
- package/dist/constants/diagnostician.js +0 -4
- package/dist/core/control-ui-db.d.ts +27 -0
- package/dist/core/control-ui-db.js +18 -0
- package/dist/core/path-resolver.js +2 -1
- package/dist/core/trajectory.d.ts +60 -0
- package/dist/core/trajectory.js +72 -2
- package/dist/hooks/bash-risk.d.ts +57 -0
- package/dist/hooks/bash-risk.js +137 -0
- package/dist/hooks/edit-verification.d.ts +62 -0
- package/dist/hooks/edit-verification.js +256 -0
- package/dist/hooks/gate-block-helper.d.ts +44 -0
- package/dist/hooks/gate-block-helper.js +119 -0
- package/dist/hooks/gate.d.ts +18 -0
- package/dist/hooks/gate.js +62 -751
- package/dist/hooks/gfi-gate.d.ts +40 -0
- package/dist/hooks/gfi-gate.js +112 -0
- package/dist/hooks/progressive-trust-gate.d.ts +79 -0
- package/dist/hooks/progressive-trust-gate.js +242 -0
- package/dist/hooks/prompt.js +10 -6
- package/dist/hooks/thinking-checkpoint.d.ts +37 -0
- package/dist/hooks/thinking-checkpoint.js +51 -0
- package/dist/http/principles-console-route.js +13 -3
- package/dist/service/central-database.js +2 -1
- package/dist/service/control-ui-query-service.d.ts +1 -1
- package/dist/service/control-ui-query-service.js +3 -3
- package/dist/service/evolution-query-service.d.ts +1 -1
- package/dist/service/evolution-query-service.js +5 -5
- package/dist/service/evolution-worker.d.ts +10 -0
- package/dist/service/evolution-worker.js +7 -3
- package/dist/service/phase3-input-filter.d.ts +57 -0
- package/dist/service/phase3-input-filter.js +93 -3
- package/dist/service/runtime-summary-service.d.ts +34 -0
- package/dist/service/runtime-summary-service.js +93 -1
- package/dist/types/event-types.d.ts +2 -0
- package/dist/types/runtime-summary.d.ts +54 -0
- package/dist/types/runtime-summary.js +1 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -210,7 +210,7 @@ export declare class ControlUiQueryService {
|
|
|
210
210
|
private readonly uiDb;
|
|
211
211
|
constructor(workspaceDir: string);
|
|
212
212
|
dispose(): void;
|
|
213
|
-
getOverview(): OverviewResponse;
|
|
213
|
+
getOverview(days?: number): OverviewResponse;
|
|
214
214
|
listSamples(filters?: SampleListFilters): SamplesResponse;
|
|
215
215
|
getSampleDetail(sampleId: string): SampleDetailResponse | null;
|
|
216
216
|
reviewSample(sampleId: string, decision: 'approved' | 'rejected', note?: string): import("../core/trajectory.js").CorrectionSampleRecord;
|
|
@@ -43,7 +43,7 @@ export class ControlUiQueryService {
|
|
|
43
43
|
dispose() {
|
|
44
44
|
this.uiDb.dispose();
|
|
45
45
|
}
|
|
46
|
-
getOverview() {
|
|
46
|
+
getOverview(days = 30) {
|
|
47
47
|
const stats = this.trajectory.getDataStats();
|
|
48
48
|
const regressionRows = this.uiDb.all('SELECT tool_name, error_type, occurrences FROM v_error_clusters ORDER BY occurrences DESC LIMIT 5');
|
|
49
49
|
const failureStats = this.uiDb.get(`
|
|
@@ -92,8 +92,8 @@ export class ControlUiQueryService {
|
|
|
92
92
|
FROM v_daily_metrics dm
|
|
93
93
|
LEFT JOIN thinking_daily td ON td.day = dm.day
|
|
94
94
|
ORDER BY dm.day DESC
|
|
95
|
-
LIMIT
|
|
96
|
-
|
|
95
|
+
LIMIT ?
|
|
96
|
+
`, days).reverse();
|
|
97
97
|
const counters = Object.fromEntries(sampleCounters.map((row) => [row.review_status, Number(row.total)]));
|
|
98
98
|
const activeModels = this.uiDb.get('SELECT COUNT(DISTINCT model_id) AS count FROM thinking_model_events')?.count ?? 0;
|
|
99
99
|
return {
|
|
@@ -186,19 +186,19 @@ export class EvolutionQueryService {
|
|
|
186
186
|
/**
|
|
187
187
|
* 获取统计数据
|
|
188
188
|
*/
|
|
189
|
-
getStats() {
|
|
189
|
+
getStats(days = 30) {
|
|
190
190
|
// 获取基础统计
|
|
191
191
|
const stats = this.trajectory.getEvolutionStats();
|
|
192
|
-
//
|
|
192
|
+
// 获取近期活动
|
|
193
193
|
const now = new Date();
|
|
194
|
-
const
|
|
194
|
+
const daysAgo = new Date(now.getTime() - days * 24 * 60 * 60 * 1000);
|
|
195
195
|
const recentTasks = this.trajectory.listEvolutionTasks({
|
|
196
|
-
dateFrom:
|
|
196
|
+
dateFrom: daysAgo.toISOString(),
|
|
197
197
|
limit: 10000,
|
|
198
198
|
});
|
|
199
199
|
// 按天分组
|
|
200
200
|
const activityByDay = new Map();
|
|
201
|
-
for (let i = 0; i <
|
|
201
|
+
for (let i = 0; i < days; i++) {
|
|
202
202
|
const day = new Date(now.getTime() - i * 24 * 60 * 60 * 1000);
|
|
203
203
|
const dayStr = day.toISOString().split('T')[0];
|
|
204
204
|
activityByDay.set(dayStr, { created: 0, completed: 0 });
|
|
@@ -44,6 +44,16 @@ export declare function registerEvolutionTaskSession(workspaceResolve: (key: str
|
|
|
44
44
|
warn?: (message: string) => void;
|
|
45
45
|
info?: (message: string) => void;
|
|
46
46
|
}): Promise<boolean>;
|
|
47
|
+
/**
|
|
48
|
+
* Evolution Worker - Background service for pain processing and evolution task management.
|
|
49
|
+
*
|
|
50
|
+
* IMPORTANT: evolution_directive.json is a COMPATIBILITY-ONLY DISPLAY ARTIFACT.
|
|
51
|
+
* This service does NOT read or use directive for Phase 3 eligibility or any decisions.
|
|
52
|
+
* Queue (EVOLUTION_QUEUE) is the only authoritative execution truth source.
|
|
53
|
+
*
|
|
54
|
+
* Directive exists solely for UI/backwards compatibility display purposes.
|
|
55
|
+
* Production evidence shows directive stopped updating on 2026-03-22 and is stale.
|
|
56
|
+
*/
|
|
47
57
|
export interface ExtendedEvolutionWorkerService {
|
|
48
58
|
id: string;
|
|
49
59
|
api: OpenClawPluginApi | null;
|
|
@@ -11,7 +11,9 @@ import { initPersistence, flushAllSessions } from '../core/session-tracker.js';
|
|
|
11
11
|
import { acquireLockAsync, releaseLock } from '../utils/file-lock.js';
|
|
12
12
|
import { getEvolutionLogger } from '../core/evolution-logger.js';
|
|
13
13
|
import { DIAGNOSTICIAN_PROTOCOL_SUMMARY } from '../constants/diagnostician.js';
|
|
14
|
+
import { LockUnavailableError } from '../config/index.js';
|
|
14
15
|
let intervalId = null;
|
|
16
|
+
let timeoutId = null;
|
|
15
17
|
const PAIN_QUEUE_DEDUP_WINDOW_MS = 30 * 60 * 1000;
|
|
16
18
|
// P0 fix: File lock constants and helper for queue operations (prevents TOCTOU race)
|
|
17
19
|
export const EVOLUTION_QUEUE_LOCK_SUFFIX = '.lock';
|
|
@@ -84,8 +86,8 @@ async function requireQueueLock(resourcePath, logger, scope, lockSuffix = EVOLUT
|
|
|
84
86
|
try {
|
|
85
87
|
return await acquireQueueLock(resourcePath, logger, lockSuffix);
|
|
86
88
|
}
|
|
87
|
-
catch {
|
|
88
|
-
throw new
|
|
89
|
+
catch (err) {
|
|
90
|
+
throw new LockUnavailableError(resourcePath, scope, { cause: err });
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
93
|
export function extractEvolutionTaskId(task) {
|
|
@@ -638,7 +640,7 @@ export const EvolutionWorkerService = {
|
|
|
638
640
|
logger.error(`[PD:EvolutionWorker] Error in worker interval: ${String(err)}`);
|
|
639
641
|
});
|
|
640
642
|
}, interval);
|
|
641
|
-
setTimeout(() => {
|
|
643
|
+
timeoutId = setTimeout(() => {
|
|
642
644
|
void (async () => {
|
|
643
645
|
await checkPainFlag(wctx, logger);
|
|
644
646
|
await processEvolutionQueue(wctx, logger, eventLog);
|
|
@@ -657,6 +659,8 @@ export const EvolutionWorkerService = {
|
|
|
657
659
|
ctx.logger.info('[PD:EvolutionWorker] Stopping background service...');
|
|
658
660
|
if (intervalId)
|
|
659
661
|
clearInterval(intervalId);
|
|
662
|
+
if (timeoutId)
|
|
663
|
+
clearTimeout(timeoutId);
|
|
660
664
|
flushAllSessions();
|
|
661
665
|
}
|
|
662
666
|
};
|
|
@@ -1,8 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3 Input Filter
|
|
3
|
+
*
|
|
4
|
+
* CRITICAL: evaluatePhase3Inputs() does NOT read or use evolution_directive.json
|
|
5
|
+
* Directive is a compatibility-only display artifact, not a truth source.
|
|
6
|
+
*
|
|
7
|
+
* Phase 3 eligibility depends ONLY on:
|
|
8
|
+
* - Queue truth (valid evolution samples from queue)
|
|
9
|
+
* - Trust input (frozen trust scorecard)
|
|
10
|
+
*
|
|
11
|
+
* Any directive file is ignored for eligibility decisions.
|
|
12
|
+
*
|
|
13
|
+
* THREE-LANE CLASSIFICATION:
|
|
14
|
+
* - authoritative: Valid inputs that can be used for Phase 3 eligibility decisions
|
|
15
|
+
* - reference_only: Valid evidence that must NOT be used as positive eligibility input
|
|
16
|
+
* (e.g., timeout-only outcomes - they indicate completion but not success)
|
|
17
|
+
* - rejected: Invalid, corrupt, or policy-prohibited input
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Classification for Phase 3 inputs.
|
|
21
|
+
* - authoritative: Can be used for Phase 3 eligibility decisions
|
|
22
|
+
* - reference_only: Valid data but not for eligibility (e.g., timeout outcomes)
|
|
23
|
+
* - rejected: Invalid or corrupt data
|
|
24
|
+
*/
|
|
25
|
+
export type Phase3Classification = 'authoritative' | 'reference_only' | 'rejected';
|
|
26
|
+
/**
|
|
27
|
+
* Classification for trust input state.
|
|
28
|
+
* - authoritative: Frozen trust with valid score
|
|
29
|
+
* - unknown: Missing trust score (not silently coerced to 0)
|
|
30
|
+
* - rejected: Unfrozen trust or invalid schema
|
|
31
|
+
*/
|
|
32
|
+
export type TrustClassification = 'authoritative' | 'unknown' | 'rejected';
|
|
1
33
|
export interface Phase3EvolutionInput {
|
|
2
34
|
id?: string | null;
|
|
3
35
|
status?: string | null;
|
|
4
36
|
started_at?: string | null;
|
|
5
37
|
completed_at?: string | null;
|
|
38
|
+
resolution?: string | null;
|
|
6
39
|
}
|
|
7
40
|
export interface Phase3TrustInput {
|
|
8
41
|
score?: number | null;
|
|
@@ -15,6 +48,12 @@ export interface Phase3EvolutionSample {
|
|
|
15
48
|
startedAt: string | null;
|
|
16
49
|
completedAt: string | null;
|
|
17
50
|
}
|
|
51
|
+
export interface Phase3ReferenceOnlySample {
|
|
52
|
+
taskId: string;
|
|
53
|
+
status: string;
|
|
54
|
+
classification: 'timeout_only' | 'other_reference';
|
|
55
|
+
reason: string;
|
|
56
|
+
}
|
|
18
57
|
export interface Phase3RejectedEvolutionSample {
|
|
19
58
|
taskId: string | null;
|
|
20
59
|
status: string | null;
|
|
@@ -22,6 +61,7 @@ export interface Phase3RejectedEvolutionSample {
|
|
|
22
61
|
}
|
|
23
62
|
export interface Phase3TrustResult {
|
|
24
63
|
eligible: boolean;
|
|
64
|
+
classification: TrustClassification;
|
|
25
65
|
rejectedReasons: string[];
|
|
26
66
|
}
|
|
27
67
|
export interface Phase3InputFilterResult {
|
|
@@ -30,8 +70,25 @@ export interface Phase3InputFilterResult {
|
|
|
30
70
|
phase3ShadowEligible: boolean;
|
|
31
71
|
evolution: {
|
|
32
72
|
eligible: Phase3EvolutionSample[];
|
|
73
|
+
referenceOnly: Phase3ReferenceOnlySample[];
|
|
33
74
|
rejected: Phase3RejectedEvolutionSample[];
|
|
34
75
|
};
|
|
35
76
|
trust: Phase3TrustResult;
|
|
36
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Evaluates Phase 3 readiness based on queue and trust inputs.
|
|
80
|
+
*
|
|
81
|
+
* IMPORTANT: Does NOT use evolution_directive.json.
|
|
82
|
+
* Directive is compatibility-only display artifact, not a truth source.
|
|
83
|
+
* Queue is the only authoritative execution truth source for Phase 3.
|
|
84
|
+
*
|
|
85
|
+
* THREE-LANE CLASSIFICATION:
|
|
86
|
+
* - authoritative: Valid inputs for Phase 3 eligibility
|
|
87
|
+
* - reference_only: Valid evidence but not for eligibility (e.g., timeout outcomes)
|
|
88
|
+
* - rejected: Invalid, corrupt, or policy-prohibited input
|
|
89
|
+
*
|
|
90
|
+
* @param queue - Evolution queue items to validate
|
|
91
|
+
* @param trust - Trust input (frozen scorecard)
|
|
92
|
+
* @returns Phase 3 eligibility results
|
|
93
|
+
*/
|
|
37
94
|
export declare function evaluatePhase3Inputs(queue: Phase3EvolutionInput[], trust: Phase3TrustInput): Phase3InputFilterResult;
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3 Input Filter
|
|
3
|
+
*
|
|
4
|
+
* CRITICAL: evaluatePhase3Inputs() does NOT read or use evolution_directive.json
|
|
5
|
+
* Directive is a compatibility-only display artifact, not a truth source.
|
|
6
|
+
*
|
|
7
|
+
* Phase 3 eligibility depends ONLY on:
|
|
8
|
+
* - Queue truth (valid evolution samples from queue)
|
|
9
|
+
* - Trust input (frozen trust scorecard)
|
|
10
|
+
*
|
|
11
|
+
* Any directive file is ignored for eligibility decisions.
|
|
12
|
+
*
|
|
13
|
+
* THREE-LANE CLASSIFICATION:
|
|
14
|
+
* - authoritative: Valid inputs that can be used for Phase 3 eligibility decisions
|
|
15
|
+
* - reference_only: Valid evidence that must NOT be used as positive eligibility input
|
|
16
|
+
* (e.g., timeout-only outcomes - they indicate completion but not success)
|
|
17
|
+
* - rejected: Invalid, corrupt, or policy-prohibited input
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Legacy queue statuses that are rejected for Phase 3
|
|
21
|
+
*/
|
|
22
|
+
const LEGACY_QUEUE_STATUSES = ['resolved', 'blocked', 'failed', 'cancelled', 'paused'];
|
|
1
23
|
function normalizeTaskId(value) {
|
|
2
24
|
if (typeof value !== 'string')
|
|
3
25
|
return null;
|
|
@@ -12,6 +34,13 @@ function normalizeStatus(value) {
|
|
|
12
34
|
return 'pending';
|
|
13
35
|
return null;
|
|
14
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Checks if a status is a legacy value that should be rejected
|
|
39
|
+
*/
|
|
40
|
+
function isLegacyStatus(value) {
|
|
41
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
42
|
+
return LEGACY_QUEUE_STATUSES.includes(normalized);
|
|
43
|
+
}
|
|
15
44
|
function normalizeTimestamp(value) {
|
|
16
45
|
if (typeof value !== 'string')
|
|
17
46
|
return null;
|
|
@@ -24,8 +53,32 @@ function normalizeTimestamp(value) {
|
|
|
24
53
|
function dedupe(values) {
|
|
25
54
|
return [...new Set(values)];
|
|
26
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a task has a timeout-only outcome (resolution indicates only timeout)
|
|
58
|
+
*/
|
|
59
|
+
function isTimeoutOnlyOutcome(item) {
|
|
60
|
+
const resolution = typeof item?.resolution === 'string' ? item.resolution.trim().toLowerCase() : '';
|
|
61
|
+
return resolution === 'auto_completed_timeout';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Evaluates Phase 3 readiness based on queue and trust inputs.
|
|
65
|
+
*
|
|
66
|
+
* IMPORTANT: Does NOT use evolution_directive.json.
|
|
67
|
+
* Directive is compatibility-only display artifact, not a truth source.
|
|
68
|
+
* Queue is the only authoritative execution truth source for Phase 3.
|
|
69
|
+
*
|
|
70
|
+
* THREE-LANE CLASSIFICATION:
|
|
71
|
+
* - authoritative: Valid inputs for Phase 3 eligibility
|
|
72
|
+
* - reference_only: Valid evidence but not for eligibility (e.g., timeout outcomes)
|
|
73
|
+
* - rejected: Invalid, corrupt, or policy-prohibited input
|
|
74
|
+
*
|
|
75
|
+
* @param queue - Evolution queue items to validate
|
|
76
|
+
* @param trust - Trust input (frozen scorecard)
|
|
77
|
+
* @returns Phase 3 eligibility results
|
|
78
|
+
*/
|
|
27
79
|
export function evaluatePhase3Inputs(queue, trust) {
|
|
28
80
|
const eligible = [];
|
|
81
|
+
const referenceOnly = [];
|
|
29
82
|
const rejected = [];
|
|
30
83
|
const taskIdCounts = new Map();
|
|
31
84
|
for (const item of queue) {
|
|
@@ -40,13 +93,22 @@ export function evaluatePhase3Inputs(queue, trust) {
|
|
|
40
93
|
const reasons = [];
|
|
41
94
|
const startedAt = normalizeTimestamp(item?.started_at);
|
|
42
95
|
const completedAt = normalizeTimestamp(item?.completed_at);
|
|
96
|
+
// Check for legacy statuses first (before other status validation)
|
|
97
|
+
if (isLegacyStatus(item?.status)) {
|
|
98
|
+
reasons.push('legacy_queue_status');
|
|
99
|
+
}
|
|
100
|
+
// Check for null status separately
|
|
101
|
+
if (item?.status === null) {
|
|
102
|
+
reasons.push('missing_status');
|
|
103
|
+
}
|
|
43
104
|
if (!taskId) {
|
|
44
105
|
reasons.push('missing_task_id');
|
|
45
106
|
}
|
|
46
107
|
else if ((taskIdCounts.get(taskId) ?? 0) > 1) {
|
|
47
108
|
reasons.push('reused_task_id');
|
|
48
109
|
}
|
|
49
|
-
if
|
|
110
|
+
// Only add invalid_status if it's not a legacy status and not null
|
|
111
|
+
if (!status && !isLegacyStatus(item?.status) && item?.status !== null) {
|
|
50
112
|
reasons.push('invalid_status');
|
|
51
113
|
}
|
|
52
114
|
if (typeof item?.started_at === 'string' && item.started_at.trim() && !startedAt) {
|
|
@@ -61,6 +123,7 @@ export function evaluatePhase3Inputs(queue, trust) {
|
|
|
61
123
|
if (status === 'completed' && !completedAt) {
|
|
62
124
|
reasons.push('missing_completed_at');
|
|
63
125
|
}
|
|
126
|
+
// Handle rejected items first
|
|
64
127
|
if (reasons.length > 0) {
|
|
65
128
|
rejected.push({
|
|
66
129
|
taskId,
|
|
@@ -72,6 +135,18 @@ export function evaluatePhase3Inputs(queue, trust) {
|
|
|
72
135
|
if (!taskId || !status) {
|
|
73
136
|
continue;
|
|
74
137
|
}
|
|
138
|
+
// Check for timeout-only outcomes (REFERENCE_ONLY, not rejected)
|
|
139
|
+
// These are valid completions but shouldn't count as positive evidence
|
|
140
|
+
if (status === 'completed' && isTimeoutOnlyOutcome(item)) {
|
|
141
|
+
referenceOnly.push({
|
|
142
|
+
taskId,
|
|
143
|
+
status: 'completed',
|
|
144
|
+
classification: 'timeout_only',
|
|
145
|
+
reason: 'Task completed via timeout - valid execution but not positive capability evidence',
|
|
146
|
+
});
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
// Valid eligible sample
|
|
75
150
|
eligible.push({
|
|
76
151
|
taskId,
|
|
77
152
|
status,
|
|
@@ -79,27 +154,42 @@ export function evaluatePhase3Inputs(queue, trust) {
|
|
|
79
154
|
completedAt,
|
|
80
155
|
});
|
|
81
156
|
}
|
|
157
|
+
// Trust classification logic
|
|
82
158
|
const trustRejectedReasons = [];
|
|
83
159
|
const score = typeof trust.score === 'number' && Number.isFinite(trust.score) ? trust.score : null;
|
|
160
|
+
let trustClassification = 'authoritative';
|
|
84
161
|
if (trust.frozen !== true) {
|
|
85
162
|
trustRejectedReasons.push('legacy_or_unfrozen_trust_schema');
|
|
163
|
+
trustClassification = 'rejected';
|
|
86
164
|
}
|
|
87
165
|
if (score === null) {
|
|
88
166
|
trustRejectedReasons.push('missing_trust_score');
|
|
167
|
+
// Missing score = unknown (unless already rejected for unfrozen)
|
|
168
|
+
if (trustClassification !== 'rejected') {
|
|
169
|
+
trustClassification = 'unknown';
|
|
170
|
+
}
|
|
89
171
|
}
|
|
90
172
|
const trustInputReady = trustRejectedReasons.length === 0;
|
|
91
|
-
|
|
92
|
-
|
|
173
|
+
// Queue is ready when:
|
|
174
|
+
// 1. Queue has items
|
|
175
|
+
// 2. No invalid/corrupt items (rejected is empty)
|
|
176
|
+
// 3. Either eligible OR referenceOnly has items (valid data exists)
|
|
177
|
+
// Note: referenceOnly (timeout outcomes) is valid data, just not positive evidence
|
|
178
|
+
const hasValidData = eligible.length > 0 || referenceOnly.length > 0;
|
|
179
|
+
const queueTruthReady = queue.length > 0 && rejected.length === 0 && hasValidData;
|
|
180
|
+
const phase3ShadowEligible = queueTruthReady && trustInputReady && eligible.length > 0;
|
|
93
181
|
return {
|
|
94
182
|
queueTruthReady,
|
|
95
183
|
trustInputReady,
|
|
96
184
|
phase3ShadowEligible,
|
|
97
185
|
evolution: {
|
|
98
186
|
eligible,
|
|
187
|
+
referenceOnly,
|
|
99
188
|
rejected,
|
|
100
189
|
},
|
|
101
190
|
trust: {
|
|
102
191
|
eligible: trustInputReady,
|
|
192
|
+
classification: trustClassification,
|
|
103
193
|
rejectedReasons: trustRejectedReasons,
|
|
104
194
|
},
|
|
105
195
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RuntimeTruth, AnalyticsTruth } from '../types/runtime-summary.js';
|
|
1
2
|
export type RuntimeDataQuality = 'authoritative' | 'partial';
|
|
2
3
|
export type RuntimeRewardPolicy = 'frozen_all_positive' | 'frozen_atomic_positive_keep_plan_ready';
|
|
3
4
|
interface RuntimeSummarySource {
|
|
@@ -13,6 +14,17 @@ interface RuntimePainSignal {
|
|
|
13
14
|
reason: string | null;
|
|
14
15
|
}
|
|
15
16
|
export interface RuntimeSummary {
|
|
17
|
+
/**
|
|
18
|
+
* Runtime truth represents the current state of the system.
|
|
19
|
+
* Used for control decisions, Phase 3 eligibility, and real-time operations.
|
|
20
|
+
*/
|
|
21
|
+
runtime: RuntimeTruth;
|
|
22
|
+
/**
|
|
23
|
+
* Analytics truth represents historical data and aggregated metrics.
|
|
24
|
+
* Used for insights, trends, and supporting evidence (where explicitly allowed).
|
|
25
|
+
* NOT used for control decisions or Phase 3 eligibility.
|
|
26
|
+
*/
|
|
27
|
+
analytics: AnalyticsTruth;
|
|
16
28
|
gfi: {
|
|
17
29
|
current: number | null;
|
|
18
30
|
peak: number | null;
|
|
@@ -45,9 +57,19 @@ export interface RuntimeSummary {
|
|
|
45
57
|
trustInputReady: boolean;
|
|
46
58
|
phase3ShadowEligible: boolean;
|
|
47
59
|
evolutionEligible: number;
|
|
60
|
+
evolutionReferenceOnly: number;
|
|
61
|
+
evolutionReferenceOnlyReasons: string[];
|
|
48
62
|
evolutionRejected: number;
|
|
49
63
|
evolutionRejectedReasons: string[];
|
|
50
64
|
trustRejectedReasons: string[];
|
|
65
|
+
legacyDirectiveFilePresent: boolean;
|
|
66
|
+
directiveStatus: 'compatibility-only' | 'missing' | 'present';
|
|
67
|
+
directiveIgnoredReason: string;
|
|
68
|
+
/**
|
|
69
|
+
* Source of Phase 3 eligibility calculation.
|
|
70
|
+
* Should always be 'runtime_truth' - analytics not used for control decisions.
|
|
71
|
+
*/
|
|
72
|
+
eligibilitySource: 'runtime_truth';
|
|
51
73
|
};
|
|
52
74
|
pain: {
|
|
53
75
|
activeFlag: boolean;
|
|
@@ -76,6 +98,11 @@ export declare class RuntimeSummaryService {
|
|
|
76
98
|
private static selectSession;
|
|
77
99
|
private static mergeSessionSnapshots;
|
|
78
100
|
private static buildQueueStats;
|
|
101
|
+
/**
|
|
102
|
+
* Builds directive summary for compatibility display only.
|
|
103
|
+
* NOT a truth source for Phase 3 eligibility or decisions.
|
|
104
|
+
* Queue is the only authoritative execution truth source.
|
|
105
|
+
*/
|
|
79
106
|
private static buildDirectiveSummary;
|
|
80
107
|
private static readLegacyTrust;
|
|
81
108
|
private static readEvents;
|
|
@@ -94,5 +121,12 @@ export declare class RuntimeSummaryService {
|
|
|
94
121
|
private static warnOnLegacyDirectiveMismatch;
|
|
95
122
|
private static readJsonFile;
|
|
96
123
|
private static asFiniteNumber;
|
|
124
|
+
/**
|
|
125
|
+
* Read trajectory analytics data from trajectory database.
|
|
126
|
+
*
|
|
127
|
+
* Returns: Analytics data (historical metrics) aggregated from trajectory database.
|
|
128
|
+
* Not: Runtime truth or real-time queue state.
|
|
129
|
+
*/
|
|
130
|
+
private static readTrajectoryStats;
|
|
97
131
|
}
|
|
98
132
|
export {};
|
|
@@ -5,6 +5,7 @@ import { resolvePdPath } from '../core/paths.js';
|
|
|
5
5
|
import { listSessions } from '../core/session-tracker.js';
|
|
6
6
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
7
7
|
import { evaluatePhase3Inputs } from './phase3-input-filter.js';
|
|
8
|
+
import { TrajectoryRegistry } from '../core/trajectory.js';
|
|
8
9
|
const MAX_SOURCE_EVENTS = 5;
|
|
9
10
|
const LEGACY_TRUST_REWARD_POLICY = 'frozen_all_positive';
|
|
10
11
|
const GFI_PARTIAL_WARNING = 'GFI source attribution remains partial in Phase 2b because only the empathy slice is source-attributed; most non-empathy friction still lacks full per-source attribution.';
|
|
@@ -30,8 +31,11 @@ export class RuntimeSummaryService {
|
|
|
30
31
|
: [];
|
|
31
32
|
const events = this.mergeEvents(persistedEvents, bufferedEvents);
|
|
32
33
|
const dailyStats = this.readJsonFile(path.join(wctx.stateDir, 'logs', 'daily-stats.json'), warnings, false);
|
|
34
|
+
// Get most recent date from daily stats, fallback to today
|
|
33
35
|
const today = generatedAt.slice(0, 10);
|
|
34
|
-
const
|
|
36
|
+
const availableDates = Object.keys(dailyStats || {}).sort().reverse();
|
|
37
|
+
const statsDate = availableDates.length > 0 ? availableDates[0] : today;
|
|
38
|
+
const dailyGfiPeak = dailyStats?.[statsDate]?.gfi?.peak;
|
|
35
39
|
const gfiCurrent = selectedSession.session && Number.isFinite(selectedSession.session.currentGfi)
|
|
36
40
|
? Number(selectedSession.session.currentGfi)
|
|
37
41
|
: null;
|
|
@@ -50,6 +54,8 @@ export class RuntimeSummaryService {
|
|
|
50
54
|
pushWarning(warnings, 'No persisted session state was found; current session GFI is unavailable.');
|
|
51
55
|
}
|
|
52
56
|
const queue = this.readJsonFile(wctx.resolve('EVOLUTION_QUEUE'), warnings, false);
|
|
57
|
+
// compatibility-only display artifact - not a truth source for Phase 3 eligibility
|
|
58
|
+
// queue is the only authoritative execution truth source for Phase 3
|
|
53
59
|
const directive = this.readJsonFile(wctx.resolve('EVOLUTION_DIRECTIVE'), warnings, false);
|
|
54
60
|
const queueStats = this.buildQueueStats(queue);
|
|
55
61
|
const directiveSummary = this.buildDirectiveSummary(queue, directive, generatedAt, warnings);
|
|
@@ -60,7 +66,53 @@ export class RuntimeSummaryService {
|
|
|
60
66
|
const lastPainSignal = this.findLastPainSignal(events, selectedSessionId);
|
|
61
67
|
const gfiSources = this.buildGfiSources(events, selectedSessionId);
|
|
62
68
|
const gateStats = this.buildGateStats(events, selectedSessionId, warnings);
|
|
69
|
+
// Read trajectory analytics data (historical data, NOT runtime truth)
|
|
70
|
+
const trajectoryStats = this.readTrajectoryStats(workspaceDir, warnings);
|
|
71
|
+
// Build runtime truth section (current state for control decisions)
|
|
72
|
+
const activeSessionIds = sessions.map(s => s.sessionId);
|
|
73
|
+
const runtimeTruth = {
|
|
74
|
+
queueState: {
|
|
75
|
+
total: queueStats.pending + queueStats.inProgress + queueStats.completed,
|
|
76
|
+
pending: queueStats.pending,
|
|
77
|
+
inProgress: queueStats.inProgress,
|
|
78
|
+
completed: queueStats.completed,
|
|
79
|
+
lastUpdated: generatedAt,
|
|
80
|
+
},
|
|
81
|
+
activeSessions: activeSessionIds,
|
|
82
|
+
currentTrustScore: legacyTrust.phase3Input.score ?? null,
|
|
83
|
+
workspaceState: {
|
|
84
|
+
frozen: legacyTrust.phase3Input.frozen ?? null,
|
|
85
|
+
lastUpdated: legacyTrust.phase3Input.lastUpdated ?? null,
|
|
86
|
+
trustClassification: phase3Inputs.trust.classification,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
// Build analytics truth section (historical data for insights)
|
|
90
|
+
const analyticsTruth = {
|
|
91
|
+
trajectoryData: {
|
|
92
|
+
totalTasks: trajectoryStats.assistantTurns + trajectoryStats.userTurns,
|
|
93
|
+
successRate: trajectoryStats.toolCalls > 0
|
|
94
|
+
? (trajectoryStats.toolCalls - trajectoryStats.failures) / trajectoryStats.toolCalls
|
|
95
|
+
: 0,
|
|
96
|
+
timeoutRate: trajectoryStats.failures > 0
|
|
97
|
+
? trajectoryStats.failures / (trajectoryStats.assistantTurns + trajectoryStats.userTurns || 1)
|
|
98
|
+
: 0,
|
|
99
|
+
trustChanges: 0, // Not tracked in current trajectory schema
|
|
100
|
+
lastUpdated: trajectoryStats.lastIngestAt ?? generatedAt,
|
|
101
|
+
},
|
|
102
|
+
dailyStats: {
|
|
103
|
+
toolCalls: dailyStats?.[statsDate]?.toolCalls ?? 0,
|
|
104
|
+
painSignals: dailyStats?.[statsDate]?.painSignals ?? 0,
|
|
105
|
+
evolutionTasks: dailyStats?.[statsDate]?.evolutionTasks ?? 0,
|
|
106
|
+
lastUpdated: statsDate,
|
|
107
|
+
},
|
|
108
|
+
trends: {
|
|
109
|
+
sevenDay: { successRateChange: 0, toolCallVolumeChange: 0, painSignalRateChange: 0 },
|
|
110
|
+
thirtyDay: { successRateChange: 0, toolCallVolumeChange: 0, painSignalRateChange: 0 },
|
|
111
|
+
},
|
|
112
|
+
};
|
|
63
113
|
return {
|
|
114
|
+
runtime: runtimeTruth,
|
|
115
|
+
analytics: analyticsTruth,
|
|
64
116
|
gfi: {
|
|
65
117
|
current: gfiCurrent,
|
|
66
118
|
peak: gfiPeak,
|
|
@@ -78,9 +130,15 @@ export class RuntimeSummaryService {
|
|
|
78
130
|
trustInputReady: phase3Inputs.trustInputReady,
|
|
79
131
|
phase3ShadowEligible: phase3Inputs.phase3ShadowEligible,
|
|
80
132
|
evolutionEligible: phase3Inputs.evolution.eligible.length,
|
|
133
|
+
evolutionReferenceOnly: phase3Inputs.evolution.referenceOnly.length,
|
|
134
|
+
evolutionReferenceOnlyReasons: [...new Set(phase3Inputs.evolution.referenceOnly.map((entry) => entry.classification))],
|
|
81
135
|
evolutionRejected: phase3Inputs.evolution.rejected.length,
|
|
82
136
|
evolutionRejectedReasons: phase3Inputs.evolution.rejected.flatMap((entry) => entry.reasons),
|
|
83
137
|
trustRejectedReasons: phase3Inputs.trust.rejectedReasons,
|
|
138
|
+
legacyDirectiveFilePresent: directive !== null,
|
|
139
|
+
directiveStatus: directive ? 'compatibility-only' : 'missing',
|
|
140
|
+
directiveIgnoredReason: 'queue is only truth source',
|
|
141
|
+
eligibilitySource: 'runtime_truth',
|
|
84
142
|
},
|
|
85
143
|
pain: {
|
|
86
144
|
activeFlag: Object.keys(painFlag).length > 0,
|
|
@@ -168,6 +226,11 @@ export class RuntimeSummaryService {
|
|
|
168
226
|
}
|
|
169
227
|
return stats;
|
|
170
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Builds directive summary for compatibility display only.
|
|
231
|
+
* NOT a truth source for Phase 3 eligibility or decisions.
|
|
232
|
+
* Queue is the only authoritative execution truth source.
|
|
233
|
+
*/
|
|
171
234
|
static buildDirectiveSummary(queue, directive, generatedAt, warnings) {
|
|
172
235
|
const inProgressTask = this.selectInProgressTask(queue);
|
|
173
236
|
if (!inProgressTask) {
|
|
@@ -443,4 +506,33 @@ export class RuntimeSummaryService {
|
|
|
443
506
|
static asFiniteNumber(value) {
|
|
444
507
|
return Number.isFinite(value) ? Number(value) : undefined;
|
|
445
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* Read trajectory analytics data from trajectory database.
|
|
511
|
+
*
|
|
512
|
+
* Returns: Analytics data (historical metrics) aggregated from trajectory database.
|
|
513
|
+
* Not: Runtime truth or real-time queue state.
|
|
514
|
+
*/
|
|
515
|
+
static readTrajectoryStats(workspaceDir, warnings) {
|
|
516
|
+
try {
|
|
517
|
+
// Use transient database instance to avoid locking issues
|
|
518
|
+
const stats = TrajectoryRegistry.use(workspaceDir, (db) => db.getDataStats());
|
|
519
|
+
return {
|
|
520
|
+
assistantTurns: stats.assistantTurns,
|
|
521
|
+
userTurns: stats.userTurns,
|
|
522
|
+
toolCalls: stats.toolCalls,
|
|
523
|
+
failures: stats.painEvents, // Approximate failures from pain events
|
|
524
|
+
lastIngestAt: stats.lastIngestAt,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
pushWarning(warnings, `Failed to read trajectory analytics: ${error instanceof Error ? error.message : String(error)}`);
|
|
529
|
+
return {
|
|
530
|
+
assistantTurns: 0,
|
|
531
|
+
userTurns: 0,
|
|
532
|
+
toolCalls: 0,
|
|
533
|
+
failures: 0,
|
|
534
|
+
lastIngestAt: null,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
446
538
|
}
|
|
@@ -75,6 +75,8 @@ export interface GateBlockEventData {
|
|
|
75
75
|
filePath: string;
|
|
76
76
|
reason: string;
|
|
77
77
|
planStatus?: string;
|
|
78
|
+
/** Source module that triggered the block (for audit trail) */
|
|
79
|
+
blockSource?: string;
|
|
78
80
|
}
|
|
79
81
|
export interface GateBypassEventData {
|
|
80
82
|
toolName: string;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime truth represents the current state of the system.
|
|
3
|
+
* Used for control decisions, Phase 3 eligibility, and real-time operations.
|
|
4
|
+
* Sources: queue state, workspace trust scorecard, active session registry
|
|
5
|
+
*/
|
|
6
|
+
export interface RuntimeTruth {
|
|
7
|
+
queueState: {
|
|
8
|
+
total: number;
|
|
9
|
+
pending: number;
|
|
10
|
+
inProgress: number;
|
|
11
|
+
completed: number;
|
|
12
|
+
lastUpdated: string;
|
|
13
|
+
};
|
|
14
|
+
activeSessions: string[];
|
|
15
|
+
currentTrustScore: number | null;
|
|
16
|
+
workspaceState: {
|
|
17
|
+
frozen: boolean | null;
|
|
18
|
+
lastUpdated: string | null;
|
|
19
|
+
trustClassification: 'authoritative' | 'unknown' | 'rejected';
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Analytics truth represents historical data and aggregated metrics.
|
|
24
|
+
* Used for insights, trends, and supporting evidence (where explicitly allowed).
|
|
25
|
+
* NOT used for control decisions or Phase 3 eligibility.
|
|
26
|
+
* Sources: trajectory.db, daily-stats.json, control-ui DB
|
|
27
|
+
*/
|
|
28
|
+
export interface AnalyticsTruth {
|
|
29
|
+
trajectoryData: {
|
|
30
|
+
totalTasks: number;
|
|
31
|
+
successRate: number;
|
|
32
|
+
timeoutRate: number;
|
|
33
|
+
trustChanges: number;
|
|
34
|
+
lastUpdated: string;
|
|
35
|
+
};
|
|
36
|
+
dailyStats: {
|
|
37
|
+
toolCalls: number;
|
|
38
|
+
painSignals: number;
|
|
39
|
+
evolutionTasks: number;
|
|
40
|
+
lastUpdated: string;
|
|
41
|
+
};
|
|
42
|
+
trends: {
|
|
43
|
+
sevenDay: TrendMetrics;
|
|
44
|
+
thirtyDay: TrendMetrics;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Trend metrics for analytics aggregation.
|
|
49
|
+
*/
|
|
50
|
+
export interface TrendMetrics {
|
|
51
|
+
successRateChange: number;
|
|
52
|
+
toolCallVolumeChange: number;
|
|
53
|
+
painSignalRateChange: number;
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/openclaw.plugin.json
CHANGED