@haaaiawd/second-nature 0.2.4 → 0.2.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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/runtime/cli/commands/index.d.ts +4 -0
- package/runtime/cli/commands/index.js +179 -5
- package/runtime/cli/index.js +2 -0
- package/runtime/cli/ops/ops-router.js +27 -17
- package/runtime/connectors/base/contract.d.ts +1 -0
- package/runtime/connectors/base/failure-taxonomy.js +45 -26
- package/runtime/connectors/services/connector-cooldown-port.d.ts +22 -0
- package/runtime/connectors/services/connector-cooldown-port.js +123 -0
- package/runtime/connectors/services/connector-executor-adapter.js +10 -4
- package/runtime/connectors/services/credential-route-context.d.ts +3 -2
- package/runtime/connectors/services/credential-route-context.js +19 -3
- package/runtime/core/second-nature/action/action-closure-recorder.d.ts +4 -0
- package/runtime/core/second-nature/action/action-closure-recorder.js +5 -0
- package/runtime/core/second-nature/action/action-proposal-builder.js +1 -0
- package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.d.ts +2 -0
- package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.js +76 -0
- package/runtime/core/second-nature/control-plane/real-runtime-spine.d.ts +2 -0
- package/runtime/core/second-nature/control-plane/real-runtime-spine.js +1 -0
- package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.d.ts +1 -1
- package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.js +10 -5
- package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.d.ts +2 -2
- package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.js +10 -28
- package/runtime/observability/db/index.d.ts +2 -0
- package/runtime/observability/db/index.js +6 -0
- package/runtime/observability/living-loop-health-gate.d.ts +6 -2
- package/runtime/observability/living-loop-health-gate.js +52 -5
- package/runtime/observability/loop-status.d.ts +19 -0
- package/runtime/observability/loop-status.js +121 -7
- package/runtime/observability/services/heartbeat-digest-assembler.d.ts +9 -0
- package/runtime/observability/services/heartbeat-digest-assembler.js +44 -9
- package/runtime/shared/types/v8-contracts.d.ts +1 -1
- package/runtime/storage/db/index.d.ts +2 -0
- package/runtime/storage/db/index.js +28 -2
- package/runtime/storage/db/schema/v8-entities.d.ts +288 -0
- package/runtime/storage/db/schema/v8-entities.js +23 -1
- package/runtime/storage/v8-state-stores.d.ts +10 -1
- package/runtime/storage/v8-state-stores.js +86 -1
|
@@ -27,6 +27,8 @@ export interface RealRunHealthProjection {
|
|
|
27
27
|
hasRealClosure: boolean;
|
|
28
28
|
hasQuietArtifact: boolean;
|
|
29
29
|
hasDreamArtifact: boolean;
|
|
30
|
+
hasFreshImpulseContext: boolean;
|
|
31
|
+
hasProjectionFeedback: boolean;
|
|
30
32
|
missingStage?: string;
|
|
31
33
|
missingReason?: string;
|
|
32
34
|
}
|
|
@@ -38,6 +40,11 @@ export interface LoopStatusReadModel {
|
|
|
38
40
|
lastHeartbeatAt?: string;
|
|
39
41
|
stageSummaries: StageSummary[];
|
|
40
42
|
policyDeniedCount: number;
|
|
43
|
+
hardGuardDeniedCount: number;
|
|
44
|
+
cooldownReplayCount: number;
|
|
45
|
+
sourceAbsenceCount: number;
|
|
46
|
+
quietSuppressionCount: number;
|
|
47
|
+
connectorTerminalCount: number;
|
|
41
48
|
nextAction: string;
|
|
42
49
|
realRunHealth: RealRunHealthProjection;
|
|
43
50
|
}
|
|
@@ -54,4 +61,16 @@ export type LoopStatusResult = {
|
|
|
54
61
|
ok: false;
|
|
55
62
|
degraded: DegradedOperationResult;
|
|
56
63
|
};
|
|
64
|
+
export interface DenialAttribution {
|
|
65
|
+
policyDeniedCount: number;
|
|
66
|
+
hardGuardDeniedCount: number;
|
|
67
|
+
cooldownReplayCount: number;
|
|
68
|
+
sourceAbsenceCount: number;
|
|
69
|
+
quietSuppressionCount: number;
|
|
70
|
+
connectorTerminalCount: number;
|
|
71
|
+
}
|
|
72
|
+
export declare function attributeDenials(db: StateDatabase, options?: {
|
|
73
|
+
day?: string;
|
|
74
|
+
cycleWindowHours?: number;
|
|
75
|
+
}): Promise<DenialAttribution>;
|
|
57
76
|
export declare function readLoopStatus(db: StateDatabase): Promise<LoopStatusResult>;
|
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { assembleLoopStatus } from "./causal-loop-health.js";
|
|
22
22
|
import { checkRealRunHealth } from "./living-loop-health-gate.js";
|
|
23
|
+
import { readActionClosuresByDay, readConnectorCooldownState, } from "../storage/v8-state-stores.js";
|
|
23
24
|
// ───────────────────────────────────────────────────────────────
|
|
24
25
|
// Helpers
|
|
25
26
|
// ───────────────────────────────────────────────────────────────
|
|
26
|
-
function computeNextAction(overallStatus, stalledAt, realRunMissingStage, realRunMissingReason) {
|
|
27
|
+
function computeNextAction(overallStatus, stalledAt, realRunMissingStage, realRunMissingReason, attribution) {
|
|
27
28
|
// Real-run health takes precedence over generic causal health
|
|
28
29
|
if (realRunMissingStage && realRunMissingStage !== "none") {
|
|
29
30
|
return `Real-run health degraded: ${realRunMissingReason ?? `missing stage: ${realRunMissingStage}`}. Run a real heartbeat cycle or verify daily rhythm state.`;
|
|
@@ -54,6 +55,110 @@ function computeNextAction(overallStatus, stalledAt, realRunMissingStage, realRu
|
|
|
54
55
|
return "Review loop stage events and state database health.";
|
|
55
56
|
}
|
|
56
57
|
// ───────────────────────────────────────────────────────────────
|
|
58
|
+
// Denial / replay attribution (T-OBS.R.4)
|
|
59
|
+
// ───────────────────────────────────────────────────────────────
|
|
60
|
+
const CONNECTOR_TERMINAL_REASONS = new Set([
|
|
61
|
+
"auth_failure",
|
|
62
|
+
"credential_expired",
|
|
63
|
+
"verification_required",
|
|
64
|
+
"configuration_missing",
|
|
65
|
+
"platform_unavailable",
|
|
66
|
+
"transport_failure",
|
|
67
|
+
"rate_limited",
|
|
68
|
+
"timeout",
|
|
69
|
+
"script_error",
|
|
70
|
+
"parse_failure",
|
|
71
|
+
"protocol_mismatch",
|
|
72
|
+
"semantic_rejection",
|
|
73
|
+
"permanent_input_error",
|
|
74
|
+
"unknown_platform_change",
|
|
75
|
+
]);
|
|
76
|
+
function classifyReasonToTerminal(reason) {
|
|
77
|
+
return CONNECTOR_TERMINAL_REASONS.has(reason);
|
|
78
|
+
}
|
|
79
|
+
function emptyAttribution() {
|
|
80
|
+
return {
|
|
81
|
+
policyDeniedCount: 0,
|
|
82
|
+
hardGuardDeniedCount: 0,
|
|
83
|
+
cooldownReplayCount: 0,
|
|
84
|
+
sourceAbsenceCount: 0,
|
|
85
|
+
quietSuppressionCount: 0,
|
|
86
|
+
connectorTerminalCount: 0,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export async function attributeDenials(db, options) {
|
|
90
|
+
const targetDay = options?.day ?? new Date().toISOString().slice(0, 10);
|
|
91
|
+
const readResult = await readActionClosuresByDay(db, targetDay);
|
|
92
|
+
if (readResult.degraded) {
|
|
93
|
+
return emptyAttribution();
|
|
94
|
+
}
|
|
95
|
+
const attribution = emptyAttribution();
|
|
96
|
+
for (const closure of readResult.rows) {
|
|
97
|
+
const status = closure.status;
|
|
98
|
+
const reason = closure.reason ?? "";
|
|
99
|
+
// Cooldown/replay is determined from durable cooldown state per platform/capability.
|
|
100
|
+
if ((status === "denied" || status === "downgraded" || status === "deferred") &&
|
|
101
|
+
closure.platformId &&
|
|
102
|
+
closure.capabilityId) {
|
|
103
|
+
const cooldownResult = await readConnectorCooldownState(db, closure.platformId, closure.capabilityId);
|
|
104
|
+
if (cooldownResult.row?.blockedUntil && new Date(cooldownResult.row.blockedUntil) > new Date()) {
|
|
105
|
+
attribution.cooldownReplayCount += 1;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const terminalClass = classifyReasonToTerminal(reason);
|
|
110
|
+
const isCooldownReplay = reason === "cooldown_blocked" || reason === "replay_suppressed";
|
|
111
|
+
const isQuietSuppression = reason === "guidance_unavailable" || reason === "quiet_empty_input" || reason === "quiet_suppression";
|
|
112
|
+
if (isCooldownReplay) {
|
|
113
|
+
attribution.cooldownReplayCount += 1;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (status === "denied") {
|
|
117
|
+
if (reason.startsWith("policy_denied")) {
|
|
118
|
+
attribution.policyDeniedCount += 1;
|
|
119
|
+
}
|
|
120
|
+
else if (terminalClass) {
|
|
121
|
+
attribution.connectorTerminalCount += 1;
|
|
122
|
+
}
|
|
123
|
+
else if (reason === "source_refs_missing" ||
|
|
124
|
+
reason === "affordance_unavailable" ||
|
|
125
|
+
reason === "awaiting_user" ||
|
|
126
|
+
reason === "permission_missing") {
|
|
127
|
+
attribution.hardGuardDeniedCount += 1;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
attribution.policyDeniedCount += 1;
|
|
131
|
+
}
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (status === "downgraded" || status === "deferred") {
|
|
135
|
+
if (isQuietSuppression) {
|
|
136
|
+
attribution.quietSuppressionCount += 1;
|
|
137
|
+
}
|
|
138
|
+
else if (terminalClass) {
|
|
139
|
+
attribution.connectorTerminalCount += 1;
|
|
140
|
+
}
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (status === "no_action") {
|
|
144
|
+
if (reason === "evidence_batch_empty" || reason === "quiet_empty_input") {
|
|
145
|
+
attribution.sourceAbsenceCount += 1;
|
|
146
|
+
}
|
|
147
|
+
else if (terminalClass) {
|
|
148
|
+
attribution.connectorTerminalCount += 1;
|
|
149
|
+
}
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (status === "completed" || status === "failed") {
|
|
153
|
+
if (terminalClass) {
|
|
154
|
+
attribution.connectorTerminalCount += 1;
|
|
155
|
+
}
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return attribution;
|
|
160
|
+
}
|
|
161
|
+
// ───────────────────────────────────────────────────────────────
|
|
57
162
|
// Public API
|
|
58
163
|
// ───────────────────────────────────────────────────────────────
|
|
59
164
|
export async function readLoopStatus(db) {
|
|
@@ -76,6 +181,8 @@ export async function readLoopStatus(db) {
|
|
|
76
181
|
hasRealClosure: realRunResult.gate.hasRealClosure,
|
|
77
182
|
hasQuietArtifact: realRunResult.gate.hasQuietArtifact,
|
|
78
183
|
hasDreamArtifact: realRunResult.gate.hasDreamArtifact,
|
|
184
|
+
hasFreshImpulseContext: realRunResult.gate.hasFreshImpulseContext,
|
|
185
|
+
hasProjectionFeedback: realRunResult.gate.hasProjectionFeedback,
|
|
79
186
|
missingStage: realRunResult.gate.missingStage,
|
|
80
187
|
missingReason: realRunResult.gate.missingReason,
|
|
81
188
|
};
|
|
@@ -88,7 +195,9 @@ export async function readLoopStatus(db) {
|
|
|
88
195
|
hasRealClosure: false,
|
|
89
196
|
hasQuietArtifact: false,
|
|
90
197
|
hasDreamArtifact: false,
|
|
91
|
-
|
|
198
|
+
hasFreshImpulseContext: false,
|
|
199
|
+
hasProjectionFeedback: false,
|
|
200
|
+
missingReason: "Real-run health check degraded: " + (realRunResult.degraded.operatorNextAction || "unknown"),
|
|
92
201
|
};
|
|
93
202
|
}
|
|
94
203
|
// Override overallStatus based on real-run health parity
|
|
@@ -111,19 +220,24 @@ export async function readLoopStatus(db) {
|
|
|
111
220
|
stalled: s.stalled,
|
|
112
221
|
lastEventAt: s.lastEventAt,
|
|
113
222
|
}));
|
|
114
|
-
//
|
|
115
|
-
const
|
|
116
|
-
const nextAction = computeNextAction(overallStatus,
|
|
223
|
+
// T-OBS.R.4: Attribute denials and connector replay root causes
|
|
224
|
+
const attribution = await attributeDenials(db);
|
|
225
|
+
const nextAction = computeNextAction(overallStatus, stalledAt, realRunHealth.missingStage, realRunHealth.missingReason, attribution);
|
|
117
226
|
return {
|
|
118
227
|
ok: true,
|
|
119
228
|
status: {
|
|
120
229
|
ok: true,
|
|
121
230
|
overallStatus,
|
|
122
|
-
stalledAt
|
|
231
|
+
stalledAt,
|
|
123
232
|
lastCycleSequence: snapshot.lastCycleSequence,
|
|
124
233
|
lastHeartbeatAt: snapshot.lastHeartbeatAt,
|
|
125
234
|
stageSummaries,
|
|
126
|
-
policyDeniedCount,
|
|
235
|
+
policyDeniedCount: attribution.policyDeniedCount,
|
|
236
|
+
hardGuardDeniedCount: attribution.hardGuardDeniedCount,
|
|
237
|
+
cooldownReplayCount: attribution.cooldownReplayCount,
|
|
238
|
+
sourceAbsenceCount: attribution.sourceAbsenceCount,
|
|
239
|
+
quietSuppressionCount: attribution.quietSuppressionCount,
|
|
240
|
+
connectorTerminalCount: attribution.connectorTerminalCount,
|
|
127
241
|
nextAction,
|
|
128
242
|
realRunHealth,
|
|
129
243
|
},
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
* tests/integration/observability/digest-delivery.test.ts (T-OBS.C.4)
|
|
32
32
|
*/
|
|
33
33
|
import type { AppendOnlyAuditStore } from "../audit/append-only-audit-store.js";
|
|
34
|
+
import type { StateDatabase } from "../../storage/db/index.js";
|
|
34
35
|
export interface ConnectorDaySummary {
|
|
35
36
|
platformId: string;
|
|
36
37
|
capability: string;
|
|
@@ -75,6 +76,8 @@ export interface RealRunHealthDigestProjection {
|
|
|
75
76
|
hasRealClosure: boolean;
|
|
76
77
|
hasQuietArtifact: boolean;
|
|
77
78
|
hasDreamArtifact: boolean;
|
|
79
|
+
hasFreshImpulseContext: boolean;
|
|
80
|
+
hasProjectionFeedback: boolean;
|
|
78
81
|
missingStage?: string;
|
|
79
82
|
missingReason?: string;
|
|
80
83
|
}
|
|
@@ -136,6 +139,12 @@ export interface StateMemoryDigestPort {
|
|
|
136
139
|
export interface HeartbeatDigestAssemblerDeps {
|
|
137
140
|
auditStore: AppendOnlyAuditStore;
|
|
138
141
|
stateMemoryPort?: StateMemoryDigestPort;
|
|
142
|
+
/**
|
|
143
|
+
* Optional state database for real-run health evaluation (F6).
|
|
144
|
+
* When provided, generateHeartbeatDigest calls checkRealRunHealth automatically
|
|
145
|
+
* and embeds the result into digest.realRunHealth.
|
|
146
|
+
*/
|
|
147
|
+
db?: StateDatabase;
|
|
139
148
|
/**
|
|
140
149
|
* Optional delivery adapter (T-OBS.C.4).
|
|
141
150
|
* When provided, the assembled digest is passed to adapter.deliver() after assembly.
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
* tests/unit/observability/heartbeat-digest-assembler.test.ts (T-OBS.C.3)
|
|
31
31
|
* tests/integration/observability/digest-delivery.test.ts (T-OBS.C.4)
|
|
32
32
|
*/
|
|
33
|
+
import { checkRealRunHealth } from "../living-loop-health-gate.js";
|
|
33
34
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
34
35
|
function isSameDayUtc(isoTimestamp, dateStr) {
|
|
35
36
|
// dateStr: "YYYY-MM-DD"
|
|
@@ -219,6 +220,48 @@ export async function generateHeartbeatDigest(date, deps) {
|
|
|
219
220
|
quietDreamSummary = aggregateQuietDreamFromAudit(events, date);
|
|
220
221
|
}
|
|
221
222
|
const nothingSignificant = isNothingSignificant(connectorSummary, goalSummary, quietDreamSummary, healthSummary);
|
|
223
|
+
// F6: Auto-evaluate real-run health when db is provided
|
|
224
|
+
let realRunHealth = {
|
|
225
|
+
gatePassed: false,
|
|
226
|
+
contractSmokeOnly: true,
|
|
227
|
+
seededStateDetected: false,
|
|
228
|
+
hasRealClosure: false,
|
|
229
|
+
hasQuietArtifact: false,
|
|
230
|
+
hasDreamArtifact: false,
|
|
231
|
+
hasFreshImpulseContext: false,
|
|
232
|
+
hasProjectionFeedback: false,
|
|
233
|
+
missingReason: "Real-run health not evaluated — no state DB wired to digest assembler",
|
|
234
|
+
};
|
|
235
|
+
if (deps.db) {
|
|
236
|
+
const realRunResult = await checkRealRunHealth(deps.db, date);
|
|
237
|
+
if (realRunResult.ok) {
|
|
238
|
+
realRunHealth = {
|
|
239
|
+
gatePassed: realRunResult.gate.gatePassed,
|
|
240
|
+
contractSmokeOnly: realRunResult.gate.contractSmokeOnly,
|
|
241
|
+
seededStateDetected: realRunResult.gate.seededStateDetected,
|
|
242
|
+
hasRealClosure: realRunResult.gate.hasRealClosure,
|
|
243
|
+
hasQuietArtifact: realRunResult.gate.hasQuietArtifact,
|
|
244
|
+
hasDreamArtifact: realRunResult.gate.hasDreamArtifact,
|
|
245
|
+
hasFreshImpulseContext: realRunResult.gate.hasFreshImpulseContext,
|
|
246
|
+
hasProjectionFeedback: realRunResult.gate.hasProjectionFeedback,
|
|
247
|
+
missingStage: realRunResult.gate.missingStage,
|
|
248
|
+
missingReason: realRunResult.gate.missingReason,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
realRunHealth = {
|
|
253
|
+
gatePassed: false,
|
|
254
|
+
contractSmokeOnly: false,
|
|
255
|
+
seededStateDetected: false,
|
|
256
|
+
hasRealClosure: false,
|
|
257
|
+
hasQuietArtifact: false,
|
|
258
|
+
hasDreamArtifact: false,
|
|
259
|
+
hasFreshImpulseContext: false,
|
|
260
|
+
hasProjectionFeedback: false,
|
|
261
|
+
missingReason: "Real-run health check degraded: " + (realRunResult.degraded.operatorNextAction ?? "unknown"),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
222
265
|
const digest = {
|
|
223
266
|
date,
|
|
224
267
|
generatedAt,
|
|
@@ -227,15 +270,7 @@ export async function generateHeartbeatDigest(date, deps) {
|
|
|
227
270
|
goalSummary,
|
|
228
271
|
quietDreamSummary,
|
|
229
272
|
healthSummary,
|
|
230
|
-
realRunHealth
|
|
231
|
-
gatePassed: false,
|
|
232
|
-
contractSmokeOnly: true,
|
|
233
|
-
seededStateDetected: false,
|
|
234
|
-
hasRealClosure: false,
|
|
235
|
-
hasQuietArtifact: false,
|
|
236
|
-
hasDreamArtifact: false,
|
|
237
|
-
missingReason: "Real-run health not evaluated — call checkRealRunHealth before digest generation",
|
|
238
|
-
},
|
|
273
|
+
realRunHealth,
|
|
239
274
|
};
|
|
240
275
|
// T-OBS.C.4: delivery hook — attempt delivery if adapter is provided
|
|
241
276
|
if (deliveryAdapter) {
|
|
@@ -82,5 +82,5 @@ export interface DegradedOperationResult {
|
|
|
82
82
|
operatorNextAction: string;
|
|
83
83
|
retryable: boolean;
|
|
84
84
|
}
|
|
85
|
-
export type V8ReasonCode = "quiet_completed" | "quiet_empty_input" | "quiet_state_unreadable" | "quiet_validation_failed" | "dream_scheduled" | "dream_scheduler_unavailable" | "dream_started" | "dream_completed" | "dream_failed" | "dream_blocked_redaction" | "projection_candidate_created" | "projection_accepted" | "projection_rejected" | "projection_superseded" | "projection_topic_matched" | "proposal_created" | "proposal_no_action" | "proposal_missing_source_refs" | "proposal_risk_blocked" | "policy_allowed" | "policy_deferred_owner_confirmation" | "policy_downgraded_to_draft" | "policy_denied_missing_permission" | "policy_denied_high_risk" | "policy_denied_breaker_open" | "guidance_unavailable" | "closure_completed" | "closure_no_action" | "closure_denied" | "closure_deferred" | "closure_downgraded" | "closure_downgraded_without_draft" | "closure_failed" | "perception_rules_only" | "evidence_batch_empty" | "evidence_batch_truncated" | "judgment_low_confidence" | "judgment_missing_source_refs" | "source_refs_unresolved" | "state_unreadable" | "stage_event_missing" | "ingestion_no_data" | "ingestion_empty" | "ingestion_state_unreadable" | "ingestion_connector_failed" | "execution_completed" | "execution_failed" | "execution_timeout" | "execution_unavailable";
|
|
85
|
+
export type V8ReasonCode = "quiet_completed" | "quiet_empty_input" | "quiet_state_unreadable" | "quiet_validation_failed" | "dream_scheduled" | "dream_scheduler_unavailable" | "dream_started" | "dream_completed" | "dream_failed" | "dream_blocked_redaction" | "projection_candidate_created" | "projection_accepted" | "projection_rejected" | "projection_superseded" | "projection_topic_matched" | "proposal_created" | "proposal_no_action" | "proposal_missing_source_refs" | "proposal_risk_blocked" | "policy_allowed" | "policy_deferred_owner_confirmation" | "policy_downgraded_to_draft" | "policy_denied_missing_permission" | "policy_denied_high_risk" | "policy_denied_breaker_open" | "guidance_unavailable" | "closure_completed" | "closure_no_action" | "closure_denied" | "closure_deferred" | "closure_downgraded" | "closure_downgraded_without_draft" | "closure_failed" | "perception_rules_only" | "perception_contract_drift" | "evidence_batch_empty" | "evidence_batch_truncated" | "judgment_low_confidence" | "judgment_missing_source_refs" | "source_refs_unresolved" | "state_unreadable" | "stage_event_missing" | "ingestion_no_data" | "ingestion_empty" | "ingestion_state_unreadable" | "ingestion_connector_failed" | "execution_completed" | "execution_failed" | "execution_timeout" | "execution_unavailable";
|
|
86
86
|
export declare const ACTION_KIND_REGISTRY: Readonly<Record<PlatformNeutralActionKind, ActionKindMetadata>>;
|
|
@@ -5,6 +5,8 @@ export interface StateDatabase {
|
|
|
5
5
|
sqlite: Database;
|
|
6
6
|
db: ReturnType<typeof drizzle<typeof schema>>;
|
|
7
7
|
schema: typeof schema;
|
|
8
|
+
/** Persist in-memory sql.js state to disk without closing the connection. */
|
|
9
|
+
flush(): void;
|
|
8
10
|
close(): void;
|
|
9
11
|
}
|
|
10
12
|
export declare function createStateDatabase(filename?: string): StateDatabase;
|
|
@@ -197,7 +197,6 @@ const STATE_SCHEMA_SQL = `
|
|
|
197
197
|
entities_json TEXT,
|
|
198
198
|
novelty TEXT,
|
|
199
199
|
relevance REAL,
|
|
200
|
-
relevance_class TEXT,
|
|
201
200
|
summary TEXT,
|
|
202
201
|
risk_flags_json TEXT,
|
|
203
202
|
confidence REAL,
|
|
@@ -225,6 +224,8 @@ const STATE_SCHEMA_SQL = `
|
|
|
225
224
|
id TEXT PRIMARY KEY,
|
|
226
225
|
created_at TEXT NOT NULL,
|
|
227
226
|
cycle_id TEXT NOT NULL,
|
|
227
|
+
platform_id TEXT,
|
|
228
|
+
capability_id TEXT,
|
|
228
229
|
proposal_id TEXT,
|
|
229
230
|
decision_id TEXT,
|
|
230
231
|
status TEXT NOT NULL,
|
|
@@ -242,7 +243,6 @@ const STATE_SCHEMA_SQL = `
|
|
|
242
243
|
closure_count INTEGER NOT NULL DEFAULT 0,
|
|
243
244
|
memory_candidate_count INTEGER NOT NULL DEFAULT 0,
|
|
244
245
|
source_refs_json TEXT NOT NULL,
|
|
245
|
-
closure_refs_json TEXT,
|
|
246
246
|
redaction_class TEXT NOT NULL DEFAULT 'none',
|
|
247
247
|
payload_json TEXT,
|
|
248
248
|
lifecycle_status TEXT NOT NULL DEFAULT 'pending'
|
|
@@ -329,6 +329,22 @@ const STATE_SCHEMA_SQL = `
|
|
|
329
329
|
payload_json TEXT,
|
|
330
330
|
updated_at TEXT NOT NULL
|
|
331
331
|
);
|
|
332
|
+
CREATE TABLE IF NOT EXISTS connector_cooldown_state (
|
|
333
|
+
id TEXT PRIMARY KEY,
|
|
334
|
+
platform_id TEXT NOT NULL,
|
|
335
|
+
capability_id TEXT NOT NULL,
|
|
336
|
+
failure_class TEXT NOT NULL,
|
|
337
|
+
retry_after_ms INTEGER,
|
|
338
|
+
blocked_until TEXT NOT NULL,
|
|
339
|
+
failure_count INTEGER NOT NULL DEFAULT 1,
|
|
340
|
+
terminal_count INTEGER NOT NULL DEFAULT 0,
|
|
341
|
+
source_refs_json TEXT NOT NULL,
|
|
342
|
+
redaction_class TEXT NOT NULL DEFAULT 'none',
|
|
343
|
+
payload_json TEXT,
|
|
344
|
+
created_at TEXT NOT NULL,
|
|
345
|
+
updated_at TEXT NOT NULL
|
|
346
|
+
);
|
|
347
|
+
CREATE INDEX IF NOT EXISTS connector_cooldown_state_platform_capability_idx ON connector_cooldown_state(platform_id, capability_id);
|
|
332
348
|
`;
|
|
333
349
|
function resolveDbPath(filename) {
|
|
334
350
|
if (path.isAbsolute(filename) || filename === ":memory:") {
|
|
@@ -350,6 +366,10 @@ function bootstrapStateSchema(sqlite) {
|
|
|
350
366
|
function applyStateSchemaMigrations(sqlite) {
|
|
351
367
|
const migrations = [
|
|
352
368
|
"ALTER TABLE policy_records ADD COLUMN outreach_daily_budget INTEGER NOT NULL DEFAULT 2",
|
|
369
|
+
"ALTER TABLE action_closure_record ADD COLUMN platform_id TEXT",
|
|
370
|
+
"ALTER TABLE action_closure_record ADD COLUMN capability_id TEXT",
|
|
371
|
+
"ALTER TABLE connector_cooldown_state ADD COLUMN terminal_count INTEGER NOT NULL DEFAULT 0",
|
|
372
|
+
"CREATE INDEX IF NOT EXISTS connector_cooldown_state_platform_capability_idx ON connector_cooldown_state(platform_id, capability_id)",
|
|
353
373
|
];
|
|
354
374
|
for (const sql of migrations) {
|
|
355
375
|
try {
|
|
@@ -374,6 +394,12 @@ export function createStateDatabase(filename = "state.db") {
|
|
|
374
394
|
sqlite,
|
|
375
395
|
db,
|
|
376
396
|
schema,
|
|
397
|
+
flush() {
|
|
398
|
+
if (!isMemory) {
|
|
399
|
+
const data = sqlite.export();
|
|
400
|
+
fs.writeFileSync(dbPath, Buffer.from(data));
|
|
401
|
+
}
|
|
402
|
+
},
|
|
377
403
|
close() {
|
|
378
404
|
if (!isMemory) {
|
|
379
405
|
const data = sqlite.export();
|