create-principles-disciple 1.118.0 → 1.119.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/console/dist/server/index.js +17 -1
- package/console/dist/server/models/ActivationsConsoleModel.d.ts +77 -0
- package/console/dist/server/models/ActivationsConsoleModel.js +322 -4
- package/console/dist/server/models/ReceiptsConsoleModel.d.ts +42 -0
- package/console/dist/server/models/ReceiptsConsoleModel.js +160 -0
- package/console/dist/server/routes/activations.d.ts +6 -1
- package/console/dist/server/routes/activations.js +142 -1
- package/console/dist/server/routes/receipts.d.ts +8 -0
- package/console/dist/server/routes/receipts.js +63 -0
- package/console/dist/ui/App.js +6 -3
- package/console/dist/ui/api.d.ts +40 -3
- package/console/dist/ui/api.js +94 -3
- package/console/dist/ui/i18n/en.json +75 -0
- package/console/dist/ui/i18n/zh-CN.json +75 -0
- package/console/dist/ui/pages/activation/ActivationPage.js +114 -5
- package/console/dist/ui/pages/activation/ActivationValidators.js +3 -0
- package/console/dist/ui/pages/focus/FocusPage.js +16 -3
- package/console/dist/ui/pages/principles/PrincipleDetailPage.js +17 -3
- package/console/dist/ui/utils/validators.d.ts +100 -0
- package/console/dist/ui/utils/validators.js +133 -0
- package/console/dist/web/assets/app.css +34 -0
- package/console/dist/web/assets/app.js +704 -14
- package/console/package.json +2 -0
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +8 -2
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
- package/core/dist/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.d.ts +2 -0
- package/core/dist/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.js +91 -0
- package/core/dist/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.js.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.d.ts +2 -0
- package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.js +83 -0
- package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.js.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-reader.test.d.ts +2 -0
- package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-reader.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-reader.test.js +48 -0
- package/core/dist/runtime-v2/activation/__tests__/promotion-readiness-reader.test.js.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.d.ts +2 -0
- package/core/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.js +141 -0
- package/core/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.js.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.d.ts +2 -0
- package/core/dist/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.js +31 -0
- package/core/dist/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.js.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.d.ts +2 -0
- package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.js +311 -0
- package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.js.map +1 -0
- package/core/dist/runtime-v2/activation/activation-control-types.d.ts +61 -0
- package/core/dist/runtime-v2/activation/activation-control-types.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/activation-control-types.js +2 -0
- package/core/dist/runtime-v2/activation/activation-control-types.js.map +1 -0
- package/core/dist/runtime-v2/activation/index.d.ts +14 -0
- package/core/dist/runtime-v2/activation/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/index.js +7 -0
- package/core/dist/runtime-v2/activation/index.js.map +1 -1
- package/core/dist/runtime-v2/activation/openclaw-promotion-checks.d.ts +30 -0
- package/core/dist/runtime-v2/activation/openclaw-promotion-checks.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/openclaw-promotion-checks.js +124 -0
- package/core/dist/runtime-v2/activation/openclaw-promotion-checks.js.map +1 -0
- package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.d.ts +17 -0
- package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.js +55 -0
- package/core/dist/runtime-v2/activation/promotion-readiness-evaluator.js.map +1 -0
- package/core/dist/runtime-v2/activation/promotion-readiness-reader.d.ts +21 -0
- package/core/dist/runtime-v2/activation/promotion-readiness-reader.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/promotion-readiness-reader.js +44 -0
- package/core/dist/runtime-v2/activation/promotion-readiness-reader.js.map +1 -0
- package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.d.ts +104 -0
- package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.js +86 -0
- package/core/dist/runtime-v2/activation/rulecode-owner-decision-service.js.map +1 -0
- package/core/dist/runtime-v2/activation/rulecode-safety-circuit-breaker.d.ts +25 -0
- package/core/dist/runtime-v2/activation/rulecode-safety-circuit-breaker.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/rulecode-safety-circuit-breaker.js +23 -0
- package/core/dist/runtime-v2/activation/rulecode-safety-circuit-breaker.js.map +1 -0
- package/core/dist/runtime-v2/activation/rulecode-shadow-summary.d.ts +14 -0
- package/core/dist/runtime-v2/activation/rulecode-shadow-summary.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/rulecode-shadow-summary.js +57 -0
- package/core/dist/runtime-v2/activation/rulecode-shadow-summary.js.map +1 -0
- package/core/dist/runtime-v2/activation/sqlite-activation-safety-store.d.ts +40 -0
- package/core/dist/runtime-v2/activation/sqlite-activation-safety-store.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/sqlite-activation-safety-store.js +461 -0
- package/core/dist/runtime-v2/activation/sqlite-activation-safety-store.js.map +1 -0
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js +24 -6
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js.map +1 -1
- package/core/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js +19 -4
- package/core/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js.map +1 -1
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.js +10 -0
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.js.map +1 -1
- package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js +13 -0
- package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +15 -2
- package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +2 -0
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +2 -2
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +1 -1
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/legacy-rule-contract-scanner.test.js +20 -1
- package/core/dist/runtime-v2/internalization/__tests__/legacy-rule-contract-scanner.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/legacy-rule-contract-scanner.d.ts +4 -4
- package/core/dist/runtime-v2/internalization/legacy-rule-contract-scanner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/legacy-rule-contract-scanner.js +92 -5
- package/core/dist/runtime-v2/internalization/legacy-rule-contract-scanner.js.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection-schema-version.test.js +11 -11
- package/core/dist/runtime-v2/store/sqlite-connection-schema-version.test.js.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.js +111 -0
- package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
- package/host-runtime/dist/host-liveness-contract.d.ts +3 -0
- package/host-runtime/dist/host-liveness-contract.js +18 -0
- package/host-runtime/dist/index.d.ts +1 -0
- package/host-runtime/dist/index.js +1 -0
- package/host-runtime/dist/legacy-rule-contract-symbols.js +88 -1
- package/host-runtime/dist/production-rulehost-gate.js +33 -16
- package/package.json +2 -2
- package/pd-cli/dist/commands/__tests__/runtime-activation-nextaction.test.js +3 -3
- package/pd-cli/dist/commands/__tests__/runtime-activation-nextaction.test.js.map +1 -1
- package/pd-cli/dist/commands/runtime-activation.d.ts +15 -3
- package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-activation.js +160 -49
- package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
- package/plugin/dist/bundle.js +730 -565
- package/plugin/dist/core/rule-host.d.ts +0 -19
- package/plugin/dist/core/rulecode-safety-circuit.d.ts +13 -0
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
|
@@ -13,6 +13,7 @@ import { handleHealthRoute, disposeHealthModels } from './routes/health.js';
|
|
|
13
13
|
import { handlePrinciplesRoute, disposePrinciplesModels } from './routes/principles.js';
|
|
14
14
|
import { handleLifecycleRoute, disposeLifecycleModels } from './routes/lifecycle.js';
|
|
15
15
|
import { handleActivationsRoute, disposeActivationsModels } from './routes/activations.js';
|
|
16
|
+
import { handleReceiptsRoute, disposeReceiptsModels } from './routes/receipts.js';
|
|
16
17
|
import { handleApprovalsGroupedRoute, disposeApprovalsGroupedModels } from './routes/approvals-grouped.js';
|
|
17
18
|
import { handleGovernanceRoute, disposeGovernanceModels } from './routes/governance.js';
|
|
18
19
|
import { handleEvidenceChainRoute, disposeEvidenceChainModels } from './routes/evidence-chain.js';
|
|
@@ -239,6 +240,7 @@ async function initServices(workspaceDir, authConfig) {
|
|
|
239
240
|
async function closeServices() {
|
|
240
241
|
disposeFeedbackReportModels();
|
|
241
242
|
disposeFailedTasksModels();
|
|
243
|
+
disposeReceiptsModels();
|
|
242
244
|
disposeApprovalsModels();
|
|
243
245
|
disposeApprovalsGroupedModels();
|
|
244
246
|
disposeHealthModels();
|
|
@@ -307,6 +309,12 @@ function handleRequest(services) {
|
|
|
307
309
|
asyncHandler(() => handleFailedTasksRoute(req, res, { workspaceDir: services.workspaceDir, subPath, featureFlags: services.feedbackFlags }))(req, res);
|
|
308
310
|
return;
|
|
309
311
|
}
|
|
312
|
+
// PRI-533: GET /api/v1/receipts/counts, /api/v1/receipts/principles/:id
|
|
313
|
+
if (urlPath === '/api/v1/receipts' || urlPath.startsWith('/api/v1/receipts/')) {
|
|
314
|
+
const subPath = urlPath.slice('/api/v1/receipts'.length);
|
|
315
|
+
asyncHandler(() => handleReceiptsRoute(req, res, services.workspaceDir, subPath))(req, res);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
310
318
|
// GET /api/v1/approvals/grouped (MUST be before approvals catch-all)
|
|
311
319
|
if (urlPath === '/api/v1/approvals/grouped') {
|
|
312
320
|
asyncHandler(() => handleApprovalsGroupedRoute(req, res, services.workspaceDir))(req, res);
|
|
@@ -368,7 +376,15 @@ function handleRequest(services) {
|
|
|
368
376
|
// CR8: GET /api/v1/activations
|
|
369
377
|
if (urlPath === '/api/v1/activations' || urlPath.startsWith('/api/v1/activations/')) {
|
|
370
378
|
const subPath = urlPath.slice('/api/v1/activations'.length);
|
|
371
|
-
|
|
379
|
+
const ownerId = process.env.PD_OWNER_ID?.trim();
|
|
380
|
+
const credentialId = process.env.PD_OWNER_CREDENTIAL_ID?.trim();
|
|
381
|
+
const ownerActor = services.authConfig.isEnabled() && ownerId && credentialId
|
|
382
|
+
? { principal: { kind: 'configured_owner', ownerId }, authentication: { method: 'console_token', credentialId } }
|
|
383
|
+
: null;
|
|
384
|
+
asyncHandler(() => handleActivationsRoute(req, res, services.workspaceDir, subPath, {
|
|
385
|
+
ownerActor,
|
|
386
|
+
breakGlassActor: { principal: { kind: 'break_glass', reason: 'local_no_auth_emergency' }, authentication: { method: 'local_break_glass' } },
|
|
387
|
+
}))(req, res);
|
|
372
388
|
return;
|
|
373
389
|
}
|
|
374
390
|
// CR8: GET /api/v1/governance/queue
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ActivationStatusRecord, PromotionReadinessResult, ActivationControlState, ActivationDecisionRecord, GlobalRuleCodePause, OwnerPromotionActor, OwnerPromotionResult } from '@principles/core/runtime-v2';
|
|
1
2
|
/**
|
|
2
3
|
* PRI-491 — owner-observable activation record.
|
|
3
4
|
*
|
|
@@ -43,6 +44,9 @@ export interface ActivationRecord {
|
|
|
43
44
|
nextAction?: string;
|
|
44
45
|
/** Present when the activation references a non-existent artifact. */
|
|
45
46
|
warning?: string;
|
|
47
|
+
enforcement?: 'eligible' | 'safety_isolated';
|
|
48
|
+
legacyDecisionUnknown?: boolean;
|
|
49
|
+
ownerReviewDueAt?: string;
|
|
46
50
|
}
|
|
47
51
|
export interface ActivationsResponse {
|
|
48
52
|
activations: ActivationRecord[];
|
|
@@ -53,10 +57,82 @@ export interface ActivationsResponse {
|
|
|
53
57
|
/** Present when status is 'degraded' — next operator action. */
|
|
54
58
|
nextAction?: string;
|
|
55
59
|
}
|
|
60
|
+
export interface RuleCodeOwnerReview {
|
|
61
|
+
activation: ActivationStatusRecord;
|
|
62
|
+
artifact: {
|
|
63
|
+
artifactId: string;
|
|
64
|
+
digest: string;
|
|
65
|
+
sourceTaskId: string;
|
|
66
|
+
lineageArtifactIds: string[];
|
|
67
|
+
content: Record<string, unknown> | null;
|
|
68
|
+
};
|
|
69
|
+
readiness: PromotionReadinessResult;
|
|
70
|
+
controlState: ActivationControlState | null;
|
|
71
|
+
decisions: ActivationDecisionRecord[];
|
|
72
|
+
globalPause: GlobalRuleCodePause | null;
|
|
73
|
+
ownerDecisionEnabled: boolean;
|
|
74
|
+
runtimeCapability: {
|
|
75
|
+
hostRuntimeVersion: string;
|
|
76
|
+
shadowEvidence: boolean;
|
|
77
|
+
};
|
|
78
|
+
liveMetrics: RuleCodeTelemetryMetrics;
|
|
79
|
+
behaviorDrift: {
|
|
80
|
+
approvedBlockRate: number | null;
|
|
81
|
+
liveBlockRate: number | null;
|
|
82
|
+
delta: number | null;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
interface RuleCodeTelemetryWindow {
|
|
86
|
+
eligible: number | null;
|
|
87
|
+
matched: number | null;
|
|
88
|
+
blocked: number | null;
|
|
89
|
+
unhealthy: number | null;
|
|
90
|
+
circuitTrips: number;
|
|
91
|
+
toolDistribution: Record<string, number> | null;
|
|
92
|
+
}
|
|
93
|
+
interface RuleCodeTelemetryMetrics {
|
|
94
|
+
last24Hours: RuleCodeTelemetryWindow;
|
|
95
|
+
last7Days: RuleCodeTelemetryWindow;
|
|
96
|
+
representativeSamples: {
|
|
97
|
+
toolName: string;
|
|
98
|
+
decision: string;
|
|
99
|
+
pathCategory: string;
|
|
100
|
+
}[];
|
|
101
|
+
}
|
|
102
|
+
export interface OwnerMutationInput {
|
|
103
|
+
actor: OwnerPromotionActor;
|
|
104
|
+
idempotencyKey: string;
|
|
105
|
+
reasonCode: string;
|
|
106
|
+
note?: string;
|
|
107
|
+
}
|
|
56
108
|
export declare class ActivationsConsoleModel {
|
|
57
109
|
private readonly workspaceDir;
|
|
58
110
|
constructor(workspaceDir: string);
|
|
59
111
|
getActivations(): Promise<ActivationsResponse>;
|
|
112
|
+
getOwnerReview(activationId: string, ownerIdentityConfigured?: boolean): Promise<RuleCodeOwnerReview>;
|
|
113
|
+
continueObserving(activationId: string, input: OwnerMutationInput): Promise<{
|
|
114
|
+
decisionId: string;
|
|
115
|
+
}>;
|
|
116
|
+
deactivateRuleCode(activationId: string, decision: 'reject_after_shadow' | 'emergency_deactivate', input: OwnerMutationInput): Promise<{
|
|
117
|
+
activationId: string;
|
|
118
|
+
decisionId: string;
|
|
119
|
+
deactivatedAt: string;
|
|
120
|
+
}>;
|
|
121
|
+
recoverRuleCodeToShadow(activationId: string, expectedControlVersion: number, input: OwnerMutationInput): Promise<{
|
|
122
|
+
sourceActivationId: string;
|
|
123
|
+
shadowActivationId: string;
|
|
124
|
+
decisionId: string;
|
|
125
|
+
}>;
|
|
126
|
+
pauseAllRuleCode(input: OwnerMutationInput): Promise<GlobalRuleCodePause>;
|
|
127
|
+
releaseRuleCodePause(pauseId: string, expectedVersion: number, input: OwnerMutationInput): Promise<GlobalRuleCodePause>;
|
|
128
|
+
promoteRuleCode(activationId: string, expected: {
|
|
129
|
+
artifactId: string;
|
|
130
|
+
artifactDigest: string;
|
|
131
|
+
controlVersion: number;
|
|
132
|
+
confirmed: boolean;
|
|
133
|
+
}, input: OwnerMutationInput): Promise<OwnerPromotionResult>;
|
|
134
|
+
private withMutableRuleCode;
|
|
135
|
+
private requireOwnerDecisionFeature;
|
|
60
136
|
deactivateActivation(activationId: string): Promise<{
|
|
61
137
|
ok: true;
|
|
62
138
|
} | {
|
|
@@ -66,3 +142,4 @@ export declare class ActivationsConsoleModel {
|
|
|
66
142
|
}>;
|
|
67
143
|
dispose(): void;
|
|
68
144
|
}
|
|
145
|
+
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { SqliteConnection, SqliteActivationStateStore, SqlitePIArtifactStore, extractPrincipleId, extractEvidenceRefs, } from '@principles/core/runtime-v2';
|
|
1
|
+
import { SqliteConnection, SqliteActivationStateStore, SqlitePIArtifactStore, extractPrincipleId, extractEvidenceRefs, PromotionReadinessReader, RuleHostWriter, createProductionGateDeps, SqliteActivationSafetyStore, isFeatureEnabled, RuleCodeOwnerDecisionService, collectOpenClawPromotionChecks, summarizeRuleCodeShadowEvents, } from '@principles/core/runtime-v2';
|
|
2
|
+
import { OPENCLAW_HOST_LIVENESS_CONTRACT } from '@principles/host-runtime';
|
|
2
3
|
import { loadPdConfig, computeFlagsFromLoadResult } from '../config/pd-config-store.js';
|
|
3
4
|
import * as fs from 'node:fs';
|
|
4
5
|
import * as path from 'node:path';
|
|
6
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
5
7
|
/**
|
|
6
8
|
* Type guard for parsed JSON objects (rc-2-no-as-bypass).
|
|
7
9
|
* Replaces `as Record<string, unknown>` casts on untrusted contentJson.
|
|
@@ -34,6 +36,80 @@ function toSnapshot(record) {
|
|
|
34
36
|
updatedAt: record.updatedAt,
|
|
35
37
|
};
|
|
36
38
|
}
|
|
39
|
+
function makeActivationDecision(value) {
|
|
40
|
+
const { activation, artifactDigest, decision, input } = value;
|
|
41
|
+
return {
|
|
42
|
+
decisionId: `decision-${randomUUID()}`,
|
|
43
|
+
subject: { kind: 'activation', activationId: activation.activationId, artifactId: activation.artifactId, artifactDigest },
|
|
44
|
+
decision, principal: input.actor.principal, authentication: input.actor.authentication,
|
|
45
|
+
...(input.actor.operator ? { operator: input.actor.operator } : {}), reasonCode: input.reasonCode,
|
|
46
|
+
note: input.note?.trim() || null, evidenceSnapshotId: null, decidedAt: new Date().toISOString(),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function unavailableShadowSummary() {
|
|
50
|
+
return {
|
|
51
|
+
observed: null, matched: null, wouldBlock: null, wouldAllow: null,
|
|
52
|
+
requireApproval: null, autoCorrect: null, errors: null, neutralControl: null,
|
|
53
|
+
firstObservedAt: null, lastObservedAt: null,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function readRuleCodeTelemetry(workspaceDir, activationId, decisions) {
|
|
57
|
+
const logsDir = path.join(workspaceDir, '.pd', 'logs');
|
|
58
|
+
const countCircuitTrips = (minimumTime) => decisions.filter(decision => decision.decision === 'safety_isolate' && Date.parse(decision.decidedAt) >= minimumTime).length;
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
const unavailableWindow = (minimumTime) => ({ eligible: null, matched: null, blocked: null, unhealthy: null, circuitTrips: countCircuitTrips(minimumTime), toolDistribution: null });
|
|
61
|
+
if (!fs.existsSync(logsDir))
|
|
62
|
+
return { shadowSummary: unavailableShadowSummary(), metrics: { last24Hours: unavailableWindow(now - 24 * 60 * 60 * 1000), last7Days: unavailableWindow(now - 7 * 24 * 60 * 60 * 1000), representativeSamples: [] } };
|
|
63
|
+
const entries = [];
|
|
64
|
+
try {
|
|
65
|
+
for (const file of fs.readdirSync(logsDir).filter(name => /^events_.*\.jsonl$/.test(name)).sort().slice(-7)) {
|
|
66
|
+
for (const line of fs.readFileSync(path.join(logsDir, file), 'utf8').split('\n').filter(Boolean)) {
|
|
67
|
+
try {
|
|
68
|
+
entries.push(JSON.parse(line));
|
|
69
|
+
}
|
|
70
|
+
catch { /* malformed telemetry is excluded, readiness remains bounded */ }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return { shadowSummary: unavailableShadowSummary(), metrics: { last24Hours: unavailableWindow(now - 24 * 60 * 60 * 1000), last7Days: unavailableWindow(now - 7 * 24 * 60 * 60 * 1000), representativeSamples: [] } };
|
|
76
|
+
}
|
|
77
|
+
const summarizeWindow = (minimumTime) => {
|
|
78
|
+
let eligible = 0;
|
|
79
|
+
let matched = 0;
|
|
80
|
+
let blocked = 0;
|
|
81
|
+
let unhealthy = 0;
|
|
82
|
+
const tools = {};
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
if (!isRecord(entry) || typeof entry.ts !== 'string' || Date.parse(entry.ts) < minimumTime || !isRecord(entry.data) || entry.data.activationId !== activationId)
|
|
85
|
+
continue;
|
|
86
|
+
if (entry.type === 'rulehost_unhealthy') {
|
|
87
|
+
unhealthy += 1;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (entry.type !== 'rulehost_evaluated' || entry.data.activationMode !== 'live')
|
|
91
|
+
continue;
|
|
92
|
+
eligible += 1;
|
|
93
|
+
if (entry.data.matched === true)
|
|
94
|
+
matched += 1;
|
|
95
|
+
if (entry.data.decision === 'block')
|
|
96
|
+
blocked += 1;
|
|
97
|
+
if (typeof entry.data.toolName === 'string')
|
|
98
|
+
tools[entry.data.toolName] = (tools[entry.data.toolName] ?? 0) + 1;
|
|
99
|
+
}
|
|
100
|
+
return { eligible, matched, blocked, unhealthy, circuitTrips: countCircuitTrips(minimumTime), toolDistribution: tools };
|
|
101
|
+
};
|
|
102
|
+
const representativeSamples = [];
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
if (representativeSamples.length >= 6)
|
|
105
|
+
break;
|
|
106
|
+
if (!isRecord(entry) || entry.type !== 'rulehost_evaluated' || !isRecord(entry.data) || entry.data.activationId !== activationId || typeof entry.data.toolName !== 'string' || typeof entry.data.decision !== 'string')
|
|
107
|
+
continue;
|
|
108
|
+
const filePath = typeof entry.data.filePath === 'string' ? entry.data.filePath : '';
|
|
109
|
+
representativeSamples.push({ toolName: entry.data.toolName, decision: entry.data.decision, pathCategory: path.extname(filePath) || (filePath ? 'workspace_path' : 'no_path') });
|
|
110
|
+
}
|
|
111
|
+
return { shadowSummary: summarizeRuleCodeShadowEvents(entries, activationId), metrics: { last24Hours: summarizeWindow(now - 24 * 60 * 60 * 1000), last7Days: summarizeWindow(now - 7 * 24 * 60 * 60 * 1000), representativeSamples } };
|
|
112
|
+
}
|
|
37
113
|
export class ActivationsConsoleModel {
|
|
38
114
|
workspaceDir;
|
|
39
115
|
constructor(workspaceDir) {
|
|
@@ -119,6 +195,17 @@ export class ActivationsConsoleModel {
|
|
|
119
195
|
// suspended (not executing) even though they remain active in the DB.
|
|
120
196
|
const featureFlags = computeFlagsFromLoadResult(loadPdConfig(this.workspaceDir));
|
|
121
197
|
const v2FlagEnabled = featureFlags.flags.rulecode_context_v2?.enabled === true;
|
|
198
|
+
const safetyStore = new SqliteActivationSafetyStore(conn);
|
|
199
|
+
const controlByActivation = new Map();
|
|
200
|
+
const hasPromotionDecision = new Set();
|
|
201
|
+
for (const activation of allActivations) {
|
|
202
|
+
const control = await safetyStore.getControlState(activation.activationId);
|
|
203
|
+
if (control)
|
|
204
|
+
controlByActivation.set(activation.activationId, control);
|
|
205
|
+
const decisions = await safetyStore.listDecisions(activation.activationId);
|
|
206
|
+
if (decisions.some(decision => decision.decision === 'promote_live'))
|
|
207
|
+
hasPromotionDecision.add(activation.activationId);
|
|
208
|
+
}
|
|
122
209
|
const facts = allActivations.map((record) => {
|
|
123
210
|
const meta = artifactMetadata.get(record.artifactId);
|
|
124
211
|
const contextVersion = meta?.contextVersion;
|
|
@@ -141,15 +228,15 @@ export class ActivationsConsoleModel {
|
|
|
141
228
|
}
|
|
142
229
|
else if (contextVersion === 'v2' && !v2FlagEnabled) {
|
|
143
230
|
status = 'suspended_by_flag';
|
|
144
|
-
nextAction = `Enable rulecode_context_v2 flag or deactivate: pd activation deactivate --activation-id ${record.activationId}
|
|
231
|
+
nextAction = `Enable rulecode_context_v2 flag or deactivate: pd activation deactivate --activation-id ${record.activationId}`;
|
|
145
232
|
}
|
|
146
233
|
else {
|
|
147
234
|
status = 'active';
|
|
148
235
|
if (mode === 'shadow') {
|
|
149
|
-
nextAction =
|
|
236
|
+
nextAction = 'Keep shadow; promotion requires an authenticated Owner decision, immutable evidence bindings, and a passing Promotion Readiness result.';
|
|
150
237
|
}
|
|
151
238
|
else if (mode === 'live') {
|
|
152
|
-
nextAction = `pd activation deactivate --activation-id ${record.activationId}
|
|
239
|
+
nextAction = `pd activation deactivate --activation-id ${record.activationId}`;
|
|
153
240
|
}
|
|
154
241
|
else {
|
|
155
242
|
nextAction = undefined;
|
|
@@ -171,6 +258,11 @@ export class ActivationsConsoleModel {
|
|
|
171
258
|
evidenceRefs,
|
|
172
259
|
evidenceSummary,
|
|
173
260
|
nextAction,
|
|
261
|
+
enforcement: controlByActivation.get(record.activationId)?.enforcement,
|
|
262
|
+
...(mode === 'live' && !hasPromotionDecision.has(record.activationId) ? {
|
|
263
|
+
legacyDecisionUnknown: true,
|
|
264
|
+
ownerReviewDueAt: new Date(new Date(record.promotedAt ?? record.activatedAt ?? Date.now()).getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
265
|
+
} : {}),
|
|
174
266
|
};
|
|
175
267
|
if (danglingArtifactIds.has(record.artifactId)) {
|
|
176
268
|
enriched.warning = `artifact_id "${record.artifactId}" does not exist in pi_artifacts - activation is orphaned`;
|
|
@@ -198,6 +290,232 @@ export class ActivationsConsoleModel {
|
|
|
198
290
|
catch { /* best-effort */ }
|
|
199
291
|
}
|
|
200
292
|
}
|
|
293
|
+
async getOwnerReview(activationId, ownerIdentityConfigured = false) {
|
|
294
|
+
const conn = new SqliteConnection({ workspaceDir: this.workspaceDir, readonly: true });
|
|
295
|
+
try {
|
|
296
|
+
const activationStore = new SqliteActivationStateStore(conn);
|
|
297
|
+
const artifactStore = new SqlitePIArtifactStore(conn);
|
|
298
|
+
const safetyStore = new SqliteActivationSafetyStore(conn);
|
|
299
|
+
const codeActivations = await activationStore.listCodeToolHookActivations(true);
|
|
300
|
+
const matches = codeActivations.filter(record => record.activationId === activationId);
|
|
301
|
+
if (matches.length !== 1)
|
|
302
|
+
throw new Error(`Owner review requires exactly one RuleCode activation: ${activationId}`);
|
|
303
|
+
const [activation] = matches;
|
|
304
|
+
if (!activation)
|
|
305
|
+
throw new Error(`Activation not found: ${activationId}`);
|
|
306
|
+
const artifact = await artifactStore.getArtifactById(activation.artifactId);
|
|
307
|
+
if (!artifact)
|
|
308
|
+
throw new Error(`Artifact not found for activation: ${activation.artifactId}`);
|
|
309
|
+
const digest = `sha256:${createHash('sha256').update(JSON.stringify(artifact), 'utf8').digest('hex')}`;
|
|
310
|
+
const decisions = await safetyStore.listDecisions(activationId);
|
|
311
|
+
const telemetry = readRuleCodeTelemetry(this.workspaceDir, activationId, decisions);
|
|
312
|
+
const flags = computeFlagsFromLoadResult(loadPdConfig(this.workspaceDir));
|
|
313
|
+
const writer = new RuleHostWriter({ gateDeps: createProductionGateDeps(), featureFlagProbe: id => isFeatureEnabled(flags, id) });
|
|
314
|
+
const reader = new PromotionReadinessReader({
|
|
315
|
+
listCodeToolHookActivations: () => activationStore.listCodeToolHookActivations(true),
|
|
316
|
+
getArtifactById: id => artifactStore.getArtifactById(id),
|
|
317
|
+
computeArtifactDigest: value => `sha256:${createHash('sha256').update(JSON.stringify(value), 'utf8').digest('hex')}`,
|
|
318
|
+
validateProductionArtifact: value => writer.canActivate(value),
|
|
319
|
+
collectHostChecks: async (value) => {
|
|
320
|
+
const liveArtifacts = [];
|
|
321
|
+
for (const active of codeActivations) {
|
|
322
|
+
if (active.action !== 'code_tool_hook_live_activate' || active.deactivatedAt !== null)
|
|
323
|
+
continue;
|
|
324
|
+
const liveArtifact = await artifactStore.getArtifactById(active.artifactId);
|
|
325
|
+
if (liveArtifact)
|
|
326
|
+
liveArtifacts.push(liveArtifact);
|
|
327
|
+
}
|
|
328
|
+
return collectOpenClawPromotionChecks(value, {
|
|
329
|
+
ownerIdentityConfigured,
|
|
330
|
+
safetyControlsEnabled: isFeatureEnabled(flags, 'rulecode_safety_controls'),
|
|
331
|
+
hostContract: OPENCLAW_HOST_LIVENESS_CONTRACT,
|
|
332
|
+
existingLiveArtifacts: liveArtifacts,
|
|
333
|
+
validateProductionArtifact: candidate => writer.canActivate(candidate),
|
|
334
|
+
});
|
|
335
|
+
},
|
|
336
|
+
buildEvidenceSnapshot: (checks, value) => {
|
|
337
|
+
const createdAt = new Date().toISOString();
|
|
338
|
+
const artifactDigest = value
|
|
339
|
+
? `sha256:${createHash('sha256').update(JSON.stringify(value), 'utf8').digest('hex')}` : digest;
|
|
340
|
+
const body = JSON.stringify({ artifactDigest, checks, createdAt });
|
|
341
|
+
return {
|
|
342
|
+
snapshotId: `snapshot-${randomUUID()}`,
|
|
343
|
+
snapshotDigest: `sha256:${createHash('sha256').update(body, 'utf8').digest('hex')}`,
|
|
344
|
+
artifactDigest, lineageRefs: value ? [value.sourceTaskId, ...value.lineageArtifactIds] : [],
|
|
345
|
+
hostRuntimeVersion: 'openclaw-legacy@1', safetyGateResults: checks,
|
|
346
|
+
shadowSummary: telemetry.shadowSummary,
|
|
347
|
+
configurationVersion: 'pd-config-current', redaction: { version: 'v1', rawParametersStored: false }, createdAt,
|
|
348
|
+
};
|
|
349
|
+
},
|
|
350
|
+
newEvaluationId: () => `readiness-${randomUUID()}`,
|
|
351
|
+
});
|
|
352
|
+
let content = null;
|
|
353
|
+
try {
|
|
354
|
+
const parsed = JSON.parse(artifact.contentJson);
|
|
355
|
+
if (isRecord(parsed))
|
|
356
|
+
content = parsed;
|
|
357
|
+
}
|
|
358
|
+
catch { /* readiness reports validation failure */ }
|
|
359
|
+
let approvedBlockRate = null;
|
|
360
|
+
let promotion;
|
|
361
|
+
for (let index = decisions.length - 1; index >= 0; index -= 1) {
|
|
362
|
+
const candidate = decisions[index];
|
|
363
|
+
if (candidate?.decision === 'promote_live' && candidate.evidenceSnapshotId !== null) {
|
|
364
|
+
promotion = candidate;
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (promotion?.evidenceSnapshotId) {
|
|
369
|
+
const row = conn.getDb().prepare('SELECT shadow_summary FROM activation_evidence_snapshots WHERE snapshot_id = ?').get(promotion.evidenceSnapshotId);
|
|
370
|
+
if (isRecord(row) && typeof row.shadow_summary === 'string') {
|
|
371
|
+
try {
|
|
372
|
+
const summary = JSON.parse(row.shadow_summary);
|
|
373
|
+
if (isRecord(summary) && typeof summary.observed === 'number' && summary.observed > 0 && typeof summary.wouldBlock === 'number') {
|
|
374
|
+
approvedBlockRate = summary.wouldBlock / summary.observed;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
catch { /* malformed immutable evidence leaves drift unavailable */ }
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const live24 = telemetry.metrics.last24Hours;
|
|
381
|
+
const liveBlockRate = live24.eligible !== null && live24.eligible > 0 && live24.blocked !== null
|
|
382
|
+
? live24.blocked / live24.eligible
|
|
383
|
+
: null;
|
|
384
|
+
return {
|
|
385
|
+
activation,
|
|
386
|
+
artifact: { artifactId: artifact.artifactId, digest, sourceTaskId: artifact.sourceTaskId, lineageArtifactIds: [...artifact.lineageArtifactIds], content },
|
|
387
|
+
readiness: await reader.evaluate({ activationId, expectedArtifactId: artifact.artifactId, expectedArtifactDigest: digest }),
|
|
388
|
+
controlState: await safetyStore.getControlState(activationId),
|
|
389
|
+
decisions,
|
|
390
|
+
globalPause: await safetyStore.getActiveGlobalPause(),
|
|
391
|
+
ownerDecisionEnabled: isFeatureEnabled(flags, 'rulecode_owner_live_decision'),
|
|
392
|
+
runtimeCapability: {
|
|
393
|
+
hostRuntimeVersion: OPENCLAW_HOST_LIVENESS_CONTRACT.version,
|
|
394
|
+
shadowEvidence: OPENCLAW_HOST_LIVENESS_CONTRACT.supportsShadowEvidence,
|
|
395
|
+
},
|
|
396
|
+
liveMetrics: telemetry.metrics,
|
|
397
|
+
behaviorDrift: {
|
|
398
|
+
approvedBlockRate,
|
|
399
|
+
liveBlockRate,
|
|
400
|
+
delta: approvedBlockRate === null || liveBlockRate === null ? null : liveBlockRate - approvedBlockRate,
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
finally {
|
|
405
|
+
try {
|
|
406
|
+
conn.close();
|
|
407
|
+
}
|
|
408
|
+
catch { /* best effort */ }
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async continueObserving(activationId, input) {
|
|
412
|
+
this.requireOwnerDecisionFeature();
|
|
413
|
+
return this.withMutableRuleCode(activationId, async (store, activation, artifactDigest) => store.recordOwnerDecision(makeActivationDecision({ activation, artifactDigest, decision: 'continue_observing', input }), input.idempotencyKey));
|
|
414
|
+
}
|
|
415
|
+
async deactivateRuleCode(activationId, decision, input) {
|
|
416
|
+
if (decision === 'reject_after_shadow')
|
|
417
|
+
this.requireOwnerDecisionFeature();
|
|
418
|
+
return this.withMutableRuleCode(activationId, async (store, activation, artifactDigest) => store.deactivateWithDecision(makeActivationDecision({ activation, artifactDigest, decision, input }), input.idempotencyKey));
|
|
419
|
+
}
|
|
420
|
+
async recoverRuleCodeToShadow(activationId, expectedControlVersion, input) {
|
|
421
|
+
this.requireOwnerDecisionFeature();
|
|
422
|
+
return this.withMutableRuleCode(activationId, async (store, activation, artifactDigest) => store.recoverToShadow({
|
|
423
|
+
...makeActivationDecision({ activation, artifactDigest, decision: 'continue_observing', input }),
|
|
424
|
+
decision: 'recover_to_shadow',
|
|
425
|
+
}, { expectedControlVersion, newActivationId: `activation-recovery-${randomUUID()}`, idempotencyKey: input.idempotencyKey }));
|
|
426
|
+
}
|
|
427
|
+
async pauseAllRuleCode(input) {
|
|
428
|
+
const conn = new SqliteConnection({ workspaceDir: this.workspaceDir, readonly: false });
|
|
429
|
+
try {
|
|
430
|
+
const decision = {
|
|
431
|
+
decisionId: `decision-${randomUUID()}`, subject: { kind: 'all_live_rulecode' }, decision: 'global_emergency_pause',
|
|
432
|
+
principal: input.actor.principal, authentication: input.actor.authentication,
|
|
433
|
+
...(input.actor.operator ? { operator: input.actor.operator } : {}), reasonCode: input.reasonCode,
|
|
434
|
+
note: input.note ?? null, evidenceSnapshotId: null, decidedAt: new Date().toISOString(),
|
|
435
|
+
};
|
|
436
|
+
return await new SqliteActivationSafetyStore(conn).pauseAllLive(decision, `pause-${randomUUID()}`, input.idempotencyKey);
|
|
437
|
+
}
|
|
438
|
+
finally {
|
|
439
|
+
try {
|
|
440
|
+
conn.close();
|
|
441
|
+
}
|
|
442
|
+
catch { /* best effort */ }
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
async releaseRuleCodePause(pauseId, expectedVersion, input) {
|
|
446
|
+
const conn = new SqliteConnection({ workspaceDir: this.workspaceDir, readonly: false });
|
|
447
|
+
try {
|
|
448
|
+
const decision = {
|
|
449
|
+
decisionId: `decision-${randomUUID()}`, subject: { kind: 'all_live_rulecode' }, decision: 'global_emergency_pause_release',
|
|
450
|
+
principal: input.actor.principal, authentication: input.actor.authentication,
|
|
451
|
+
...(input.actor.operator ? { operator: input.actor.operator } : {}), reasonCode: input.reasonCode,
|
|
452
|
+
note: input.note ?? null, evidenceSnapshotId: null, decidedAt: new Date().toISOString(),
|
|
453
|
+
};
|
|
454
|
+
return await new SqliteActivationSafetyStore(conn).releaseGlobalPause(decision, { pauseId, expectedVersion, idempotencyKey: input.idempotencyKey });
|
|
455
|
+
}
|
|
456
|
+
finally {
|
|
457
|
+
try {
|
|
458
|
+
conn.close();
|
|
459
|
+
}
|
|
460
|
+
catch { /* best effort */ }
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async promoteRuleCode(activationId, expected, input) {
|
|
464
|
+
const review = await this.getOwnerReview(activationId, input.actor.principal.kind === 'configured_owner' && input.actor.authentication.method === 'console_token');
|
|
465
|
+
const flags = computeFlagsFromLoadResult(loadPdConfig(this.workspaceDir));
|
|
466
|
+
const conn = new SqliteConnection({ workspaceDir: this.workspaceDir, readonly: false });
|
|
467
|
+
try {
|
|
468
|
+
const store = new SqliteActivationSafetyStore(conn);
|
|
469
|
+
const service = new RuleCodeOwnerDecisionService({
|
|
470
|
+
ownerLiveDecisionEnabled: () => isFeatureEnabled(flags, 'rulecode_owner_live_decision'),
|
|
471
|
+
safetyControlsEnabled: () => isFeatureEnabled(flags, 'rulecode_safety_controls'),
|
|
472
|
+
evaluateReadiness: async () => review.readiness,
|
|
473
|
+
commitPromotion: value => store.commitPromotion(value),
|
|
474
|
+
newDecisionId: () => `decision-${randomUUID()}`,
|
|
475
|
+
now: () => new Date().toISOString(),
|
|
476
|
+
});
|
|
477
|
+
return await service.promote({
|
|
478
|
+
activationId, expectedArtifactId: expected.artifactId, expectedArtifactDigest: expected.artifactDigest,
|
|
479
|
+
expectedControlVersion: expected.controlVersion, idempotencyKey: input.idempotencyKey,
|
|
480
|
+
reasonCode: input.reasonCode, note: input.note, confirmed: expected.confirmed,
|
|
481
|
+
}, input.actor);
|
|
482
|
+
}
|
|
483
|
+
finally {
|
|
484
|
+
try {
|
|
485
|
+
conn.close();
|
|
486
|
+
}
|
|
487
|
+
catch { /* best effort */ }
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
async withMutableRuleCode(activationId, action) {
|
|
491
|
+
const conn = new SqliteConnection({ workspaceDir: this.workspaceDir, readonly: false });
|
|
492
|
+
try {
|
|
493
|
+
const activationStore = new SqliteActivationStateStore(conn);
|
|
494
|
+
const artifactStore = new SqlitePIArtifactStore(conn);
|
|
495
|
+
const matches = (await activationStore.listCodeToolHookActivations(true)).filter(value => value.activationId === activationId);
|
|
496
|
+
if (matches.length !== 1)
|
|
497
|
+
throw new Error(`RuleCode mutation requires exactly one activation: ${activationId}`);
|
|
498
|
+
const [activation] = matches;
|
|
499
|
+
if (!activation)
|
|
500
|
+
throw new Error(`Activation not found: ${activationId}`);
|
|
501
|
+
const artifact = await artifactStore.getArtifactById(activation.artifactId);
|
|
502
|
+
if (!artifact)
|
|
503
|
+
throw new Error(`Artifact not found for activation: ${activation.artifactId}`);
|
|
504
|
+
const artifactDigest = `sha256:${createHash('sha256').update(JSON.stringify(artifact), 'utf8').digest('hex')}`;
|
|
505
|
+
return await action(new SqliteActivationSafetyStore(conn), activation, artifactDigest);
|
|
506
|
+
}
|
|
507
|
+
finally {
|
|
508
|
+
try {
|
|
509
|
+
conn.close();
|
|
510
|
+
}
|
|
511
|
+
catch { /* best effort */ }
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
requireOwnerDecisionFeature() {
|
|
515
|
+
const flags = computeFlagsFromLoadResult(loadPdConfig(this.workspaceDir));
|
|
516
|
+
if (!isFeatureEnabled(flags, 'rulecode_owner_live_decision'))
|
|
517
|
+
throw new Error('feature_not_enabled: enable rulecode_owner_live_decision only after the rollout gate passes');
|
|
518
|
+
}
|
|
201
519
|
async deactivateActivation(activationId) {
|
|
202
520
|
const stateDbPath = path.join(this.workspaceDir, '.pd', 'state.db');
|
|
203
521
|
if (!fs.existsSync(stateDbPath)) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface ReceiptEvent {
|
|
2
|
+
kind: 'rule_blocked' | 'auto_correct_applied' | 'self_reported' | 'prompt_injected';
|
|
3
|
+
level: 'effect' | 'presence';
|
|
4
|
+
sessionId: string | null;
|
|
5
|
+
toolName: string | null;
|
|
6
|
+
filePath: string | null;
|
|
7
|
+
digest: string | null;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PrincipleReceipts {
|
|
11
|
+
status: 'ok' | 'degraded';
|
|
12
|
+
reason?: string;
|
|
13
|
+
nextAction?: string;
|
|
14
|
+
principleId: string;
|
|
15
|
+
effectCount: number;
|
|
16
|
+
presenceCount: number;
|
|
17
|
+
lastEffectAt: string | null;
|
|
18
|
+
events: ReceiptEvent[];
|
|
19
|
+
}
|
|
20
|
+
export interface ReceiptCountEntry {
|
|
21
|
+
principleId: string;
|
|
22
|
+
effectCount: number;
|
|
23
|
+
presenceCount: number;
|
|
24
|
+
lastEffectAt: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface ReceiptCounts {
|
|
27
|
+
status: 'ok' | 'degraded';
|
|
28
|
+
reason?: string;
|
|
29
|
+
nextAction?: string;
|
|
30
|
+
counts: ReceiptCountEntry[];
|
|
31
|
+
}
|
|
32
|
+
export declare class ReceiptsConsoleModel {
|
|
33
|
+
private readonly workspaceDir;
|
|
34
|
+
constructor(workspaceDir: string);
|
|
35
|
+
getPrincipleReceipts(principleId: string): Promise<PrincipleReceipts>;
|
|
36
|
+
getReceiptCounts(): Promise<ReceiptCounts>;
|
|
37
|
+
/**
|
|
38
|
+
* Shared preconditions: state.db exists + ledger flag enabled. Returns a
|
|
39
|
+
* degraded partial response when a precondition fails (rc-9), else null.
|
|
40
|
+
*/
|
|
41
|
+
private precheck;
|
|
42
|
+
}
|