@haaaiawd/second-nature 0.1.8 → 0.1.10
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/index.js +281 -69
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
- package/runtime/cli/commands/index.d.ts +2 -0
- package/runtime/cli/commands/index.js +61 -6
- package/runtime/cli/explain/explain-surface-subject.d.ts +8 -0
- package/runtime/cli/explain/explain-surface-subject.js +9 -0
- package/runtime/cli/explain/format-explanation.d.ts +2 -0
- package/runtime/cli/explain/format-explanation.js +2 -0
- package/runtime/cli/explain/resolve-subject.js +15 -0
- package/runtime/cli/host-capability/classify-delivery.d.ts +14 -0
- package/runtime/cli/host-capability/classify-delivery.js +20 -0
- package/runtime/cli/host-capability/probe-host-capability.d.ts +2 -0
- package/runtime/cli/host-capability/probe-host-capability.js +58 -0
- package/runtime/cli/host-capability/record-host-capability.d.ts +6 -0
- package/runtime/cli/host-capability/record-host-capability.js +14 -0
- package/runtime/cli/host-capability/types.d.ts +71 -0
- package/runtime/cli/host-capability/types.js +6 -0
- package/runtime/cli/host-smoke/run-host-smoke.d.ts +2 -0
- package/runtime/cli/host-smoke/run-host-smoke.js +40 -0
- package/runtime/cli/host-smoke/types.d.ts +35 -0
- package/runtime/cli/host-smoke/types.js +6 -0
- package/runtime/cli/index.js +18 -0
- package/runtime/cli/ops/heartbeat-surface.d.ts +35 -0
- package/runtime/cli/ops/heartbeat-surface.js +71 -0
- package/runtime/cli/ops/ops-router.d.ts +16 -0
- package/runtime/cli/ops/ops-router.js +83 -0
- package/runtime/cli/ops/show-operator-fallback.d.ts +13 -0
- package/runtime/cli/ops/show-operator-fallback.js +22 -0
- package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +10 -0
- package/runtime/cli/ops/workspace-heartbeat-runner.js +26 -0
- package/runtime/cli/read-models/index.d.ts +11 -2
- package/runtime/cli/read-models/index.js +50 -0
- package/runtime/cli/read-models/operator-explain-map.d.ts +6 -0
- package/runtime/cli/read-models/operator-explain-map.js +10 -0
- package/runtime/cli/read-models/types.d.ts +5 -1
- package/runtime/cli/runtime/runtime-artifact-boundary.d.ts +28 -0
- package/runtime/cli/runtime/runtime-artifact-boundary.js +94 -0
- package/runtime/connectors/base/contract.d.ts +6 -0
- package/runtime/connectors/base/execution-policy.d.ts +47 -0
- package/runtime/connectors/base/execution-policy.js +82 -0
- package/runtime/connectors/base/index.d.ts +2 -0
- package/runtime/connectors/base/index.js +2 -0
- package/runtime/connectors/base/manifest.d.ts +55 -2
- package/runtime/connectors/base/manifest.js +50 -0
- package/runtime/connectors/base/map-life-evidence.d.ts +16 -0
- package/runtime/connectors/base/map-life-evidence.js +79 -0
- package/runtime/connectors/base/policy-layer.d.ts +2 -0
- package/runtime/connectors/base/policy-layer.js +16 -0
- package/runtime/connectors/base/route-planner.js +1 -0
- package/runtime/connectors/index.d.ts +1 -0
- package/runtime/connectors/index.js +1 -0
- package/runtime/connectors/near-real/near-real-connector-smoke.d.ts +19 -0
- package/runtime/connectors/near-real/near-real-connector-smoke.js +152 -0
- package/runtime/core/second-nature/heartbeat/heartbeat-executor.js +2 -0
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +37 -16
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +95 -29
- package/runtime/core/second-nature/heartbeat/index.d.ts +4 -1
- package/runtime/core/second-nature/heartbeat/index.js +4 -1
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.d.ts +21 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.js +35 -0
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +28 -0
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +35 -0
- package/runtime/core/second-nature/heartbeat/signal.d.ts +9 -2
- package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +19 -1
- package/runtime/core/second-nature/index.d.ts +8 -0
- package/runtime/core/second-nature/index.js +8 -0
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +1 -1
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +9 -4
- package/runtime/core/second-nature/orchestrator/guard-layer.d.ts +6 -0
- package/runtime/core/second-nature/orchestrator/guard-layer.js +76 -20
- package/runtime/core/second-nature/orchestrator/intent-planner.d.ts +10 -0
- package/runtime/core/second-nature/orchestrator/intent-planner.js +135 -28
- package/runtime/core/second-nature/orchestrator/lease-manager.d.ts +1 -1
- package/runtime/core/second-nature/orchestrator/lease-manager.js +1 -1
- package/runtime/core/second-nature/outreach/build-outreach-draft-request.d.ts +6 -0
- package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +63 -0
- package/runtime/core/second-nature/outreach/delivery-target.d.ts +26 -0
- package/runtime/core/second-nature/outreach/delivery-target.js +70 -0
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +38 -0
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +119 -0
- package/runtime/core/second-nature/outreach/judge-input-from-snapshot.d.ts +7 -0
- package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +45 -0
- package/runtime/core/second-nature/outreach/judge-outreach.d.ts +40 -0
- package/runtime/core/second-nature/outreach/judge-outreach.js +121 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +21 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +123 -0
- package/runtime/core/second-nature/rhythm/planner-rhythm-window.d.ts +15 -0
- package/runtime/core/second-nature/rhythm/planner-rhythm-window.js +52 -0
- package/runtime/core/second-nature/rhythm/policy-bridge.d.ts +19 -0
- package/runtime/core/second-nature/rhythm/policy-bridge.js +34 -0
- package/runtime/core/second-nature/types.d.ts +16 -2
- package/runtime/guidance/draft-outreach-message.d.ts +7 -0
- package/runtime/guidance/draft-outreach-message.js +42 -0
- package/runtime/guidance/evidence-guidance.d.ts +40 -0
- package/runtime/guidance/evidence-guidance.js +52 -0
- package/runtime/guidance/index.d.ts +3 -0
- package/runtime/guidance/index.js +3 -0
- package/runtime/guidance/outreach-draft-schema.d.ts +228 -0
- package/runtime/guidance/outreach-draft-schema.js +80 -0
- package/runtime/observability/audit/append-only-audit-store.d.ts +14 -0
- package/runtime/observability/audit/append-only-audit-store.js +21 -0
- package/runtime/observability/audit/audit-envelope.d.ts +51 -0
- package/runtime/observability/audit/audit-envelope.js +130 -0
- package/runtime/observability/audit/verify-audit-hash-chain.d.ts +23 -0
- package/runtime/observability/audit/verify-audit-hash-chain.js +83 -0
- package/runtime/observability/db/index.js +11 -0
- package/runtime/observability/db/schema/host-capability-reports.d.ts +180 -0
- package/runtime/observability/db/schema/host-capability-reports.js +12 -0
- package/runtime/observability/db/schema/index.d.ts +1 -0
- package/runtime/observability/db/schema/index.js +1 -0
- package/runtime/observability/index.d.ts +7 -0
- package/runtime/observability/index.js +7 -0
- package/runtime/observability/query/explain-query.d.ts +48 -0
- package/runtime/observability/query/explain-query.js +114 -0
- package/runtime/observability/query/export-audit-bundle.d.ts +22 -0
- package/runtime/observability/query/export-audit-bundle.js +27 -0
- package/runtime/observability/services/decision-ledger.d.ts +1 -1
- package/runtime/observability/services/decision-ledger.js +4 -0
- package/runtime/observability/services/governance-audit.d.ts +14 -0
- package/runtime/observability/services/governance-audit.js +25 -1
- package/runtime/observability/services/governance-plane-recorder.d.ts +47 -0
- package/runtime/observability/services/governance-plane-recorder.js +55 -0
- package/runtime/observability/services/lived-experience-audit.d.ts +97 -0
- package/runtime/observability/services/lived-experience-audit.js +162 -0
- package/runtime/storage/bootstrap/native-sqlite-probe.d.ts +7 -0
- package/runtime/storage/bootstrap/native-sqlite-probe.js +28 -0
- package/runtime/storage/bootstrap/repair-gate.d.ts +17 -0
- package/runtime/storage/bootstrap/repair-gate.js +71 -0
- package/runtime/storage/bootstrap/storage-mode-smoke.d.ts +38 -0
- package/runtime/storage/bootstrap/storage-mode-smoke.js +85 -0
- package/runtime/storage/db/index.js +49 -0
- package/runtime/storage/db/schema/delivery-attempts.d.ts +199 -0
- package/runtime/storage/db/schema/delivery-attempts.js +13 -0
- package/runtime/storage/db/schema/index.d.ts +3 -0
- package/runtime/storage/db/schema/index.js +3 -0
- package/runtime/storage/db/schema/life-evidence-index.d.ts +161 -0
- package/runtime/storage/db/schema/life-evidence-index.js +11 -0
- package/runtime/storage/db/schema/operator-fallback-artifacts.d.ts +161 -0
- package/runtime/storage/db/schema/operator-fallback-artifacts.js +11 -0
- package/runtime/storage/db/schema/policies.d.ts +17 -0
- package/runtime/storage/db/schema/policies.js +1 -0
- package/runtime/storage/delivery/query-delivery-attempts.d.ts +3 -0
- package/runtime/storage/delivery/query-delivery-attempts.js +32 -0
- package/runtime/storage/delivery/types.d.ts +27 -0
- package/runtime/storage/delivery/types.js +1 -0
- package/runtime/storage/delivery/write-delivery-attempt.d.ts +6 -0
- package/runtime/storage/delivery/write-delivery-attempt.js +36 -0
- package/runtime/storage/fallback/load-operator-fallback.d.ts +14 -0
- package/runtime/storage/fallback/load-operator-fallback.js +47 -0
- package/runtime/storage/fallback/operator-fallback-types.d.ts +9 -0
- package/runtime/storage/fallback/operator-fallback-types.js +1 -0
- package/runtime/storage/fallback/operator-fallback-view.d.ts +11 -0
- package/runtime/storage/fallback/operator-fallback-view.js +1 -0
- package/runtime/storage/fallback/write-operator-fallback.d.ts +6 -0
- package/runtime/storage/fallback/write-operator-fallback.js +21 -0
- package/runtime/storage/index.d.ts +21 -0
- package/runtime/storage/index.js +14 -0
- package/runtime/storage/life-evidence/append-life-evidence.d.ts +7 -0
- package/runtime/storage/life-evidence/append-life-evidence.js +64 -0
- package/runtime/storage/life-evidence/types.d.ts +45 -0
- package/runtime/storage/life-evidence/types.js +6 -0
- package/runtime/storage/quiet/persist-quiet-artifact.d.ts +7 -0
- package/runtime/storage/quiet/persist-quiet-artifact.js +22 -0
- package/runtime/storage/quiet/quiet-artifact-types.d.ts +18 -0
- package/runtime/storage/quiet/quiet-artifact-types.js +1 -0
- package/runtime/storage/quiet/quiet-artifact-writer.d.ts +15 -0
- package/runtime/storage/quiet/quiet-artifact-writer.js +56 -0
- package/runtime/storage/repositories/credential-repository.js +12 -1
- package/runtime/storage/rhythm/rhythm-policy-snapshot.d.ts +10 -0
- package/runtime/storage/rhythm/rhythm-policy-snapshot.js +34 -0
- package/runtime/storage/services/credential-vault.d.ts +5 -0
- package/runtime/storage/services/credential-vault.js +47 -9
- package/runtime/storage/snapshots/continuity-snapshot.d.ts +9 -0
- package/runtime/storage/snapshots/continuity-snapshot.js +41 -0
- package/runtime/storage/snapshots/life-evidence-snapshot.d.ts +6 -0
- package/runtime/storage/snapshots/life-evidence-snapshot.js +114 -0
- package/runtime/storage/snapshots/types.d.ts +58 -0
- package/runtime/storage/snapshots/types.js +1 -0
- package/runtime/storage/state-api.js +11 -4
- package/runtime/storage/user-interest/load-user-interest-snapshot.d.ts +2 -0
- package/runtime/storage/user-interest/load-user-interest-snapshot.js +150 -0
- package/runtime/storage/user-interest/types.d.ts +25 -0
- package/runtime/storage/user-interest/types.js +1 -0
- package/workspace-ops-bridge.js +78 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const NO_USER_VISIBLE = "no_user_visible_contact_claim_prohibited";
|
|
2
|
+
function isNoUserVisibleDelivery(status) {
|
|
3
|
+
if (!status)
|
|
4
|
+
return false;
|
|
5
|
+
return (status === "target_none" ||
|
|
6
|
+
status === "not_sent_fallback" ||
|
|
7
|
+
status === "channel_missing" ||
|
|
8
|
+
status === "host_unsupported" ||
|
|
9
|
+
status === "failed" ||
|
|
10
|
+
status === "ack_dropped");
|
|
11
|
+
}
|
|
12
|
+
function payloadDecisionId(payload) {
|
|
13
|
+
if (!payload || typeof payload !== "object")
|
|
14
|
+
return undefined;
|
|
15
|
+
const p = payload;
|
|
16
|
+
const v = p.decisionId;
|
|
17
|
+
return typeof v === "string" ? v : undefined;
|
|
18
|
+
}
|
|
19
|
+
function payloadFallbackRef(payload) {
|
|
20
|
+
if (!payload || typeof payload !== "object")
|
|
21
|
+
return undefined;
|
|
22
|
+
const p = payload;
|
|
23
|
+
const v = p.fallbackRef;
|
|
24
|
+
return typeof v === "string" ? v : undefined;
|
|
25
|
+
}
|
|
26
|
+
function payloadAuditId(payload) {
|
|
27
|
+
if (!payload || typeof payload !== "object")
|
|
28
|
+
return undefined;
|
|
29
|
+
const p = payload;
|
|
30
|
+
const a = p.auditId;
|
|
31
|
+
if (typeof a === "string")
|
|
32
|
+
return a;
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
function deliveryStatusFrom(payload) {
|
|
36
|
+
if (!payload || typeof payload !== "object")
|
|
37
|
+
return undefined;
|
|
38
|
+
const p = payload;
|
|
39
|
+
const s = p.status;
|
|
40
|
+
return typeof s === "string" ? s : undefined;
|
|
41
|
+
}
|
|
42
|
+
function eventMatchesQuery(envelope, query) {
|
|
43
|
+
const payload = envelope.payload;
|
|
44
|
+
switch (query.kind) {
|
|
45
|
+
case "decision":
|
|
46
|
+
return payloadDecisionId(payload) === query.decisionId;
|
|
47
|
+
case "fallback":
|
|
48
|
+
return payloadFallbackRef(payload) === query.fallbackRef;
|
|
49
|
+
case "report":
|
|
50
|
+
return envelope.eventId === query.reportId || payloadAuditId(payload) === query.reportId;
|
|
51
|
+
case "delivery":
|
|
52
|
+
return (envelope.family === "delivery" &&
|
|
53
|
+
(envelope.eventId === query.auditId || payloadAuditId(payload) === query.auditId));
|
|
54
|
+
case "source_ref": {
|
|
55
|
+
const needle = query.sourceRefId;
|
|
56
|
+
try {
|
|
57
|
+
return JSON.stringify(payload).includes(needle);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function summarizeEnvelope(e) {
|
|
66
|
+
const fam = e.family;
|
|
67
|
+
if (fam === "delivery") {
|
|
68
|
+
const st = deliveryStatusFrom(e.payload);
|
|
69
|
+
return `delivery_audit:${st ?? "unknown"}`;
|
|
70
|
+
}
|
|
71
|
+
if (fam === "heartbeat.decision") {
|
|
72
|
+
const outcome = e.payload?.outcome;
|
|
73
|
+
return `decision:${typeof outcome === "string" ? outcome : "unknown"}`;
|
|
74
|
+
}
|
|
75
|
+
return `${fam}`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Query explain read model from an in-memory append-only audit slice (tests / local tooling).
|
|
79
|
+
*/
|
|
80
|
+
export function queryExplain(query, store) {
|
|
81
|
+
const matched = store.list().filter((e) => eventMatchesQuery(e, query));
|
|
82
|
+
const relatedEventIds = matched.map((e) => e.eventId);
|
|
83
|
+
const warnings = [];
|
|
84
|
+
let deliveryStatus;
|
|
85
|
+
for (const e of matched) {
|
|
86
|
+
if (e.family === "delivery") {
|
|
87
|
+
const st = deliveryStatusFrom(e.payload);
|
|
88
|
+
if (st)
|
|
89
|
+
deliveryStatus = st;
|
|
90
|
+
if (isNoUserVisibleDelivery(st)) {
|
|
91
|
+
warnings.push(NO_USER_VISIBLE);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const uniqueWarnings = [...new Set(warnings)];
|
|
96
|
+
const summary = matched.length === 0
|
|
97
|
+
? "no_matching_audit_events"
|
|
98
|
+
: `matched_events=${matched.length};subject=${query.kind}`;
|
|
99
|
+
const events = matched.map((e) => ({
|
|
100
|
+
eventId: e.eventId,
|
|
101
|
+
family: e.family,
|
|
102
|
+
plane: e.plane,
|
|
103
|
+
createdAt: e.createdAt,
|
|
104
|
+
summary: summarizeEnvelope(e),
|
|
105
|
+
}));
|
|
106
|
+
return {
|
|
107
|
+
query,
|
|
108
|
+
summary,
|
|
109
|
+
warnings: uniqueWarnings,
|
|
110
|
+
deliveryStatus,
|
|
111
|
+
relatedEventIds,
|
|
112
|
+
events,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AuditEnvelope } from "../audit/audit-envelope.js";
|
|
2
|
+
import type { AuditEventFamily } from "../audit/audit-envelope.js";
|
|
3
|
+
import type { AuditExportRange } from "../audit/verify-audit-hash-chain.js";
|
|
4
|
+
export interface AuditBundleExportRange extends AuditExportRange {
|
|
5
|
+
/** Reserved for otel_projection / future formats */
|
|
6
|
+
format?: "json_v1";
|
|
7
|
+
}
|
|
8
|
+
export interface AuditRedactionSummary {
|
|
9
|
+
eventCount: number;
|
|
10
|
+
manifestIds: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface AuditBundle {
|
|
13
|
+
bundleId: string;
|
|
14
|
+
generatedAt: string;
|
|
15
|
+
range: AuditBundleExportRange;
|
|
16
|
+
events: readonly AuditEnvelope<unknown>[];
|
|
17
|
+
redactionSummary: AuditRedactionSummary;
|
|
18
|
+
}
|
|
19
|
+
export interface ExportAuditBundleDeps {
|
|
20
|
+
loadRange(from: string, to: string, families?: AuditEventFamily[]): Promise<readonly AuditEnvelope<unknown>[]>;
|
|
21
|
+
}
|
|
22
|
+
export declare function exportAuditBundle(range: AuditBundleExportRange, deps: ExportAuditBundleDeps): Promise<AuditBundle>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redacted audit export bundle for operator / retention tooling (T5.3.1).
|
|
3
|
+
*
|
|
4
|
+
* Core logic: load range, attach export metadata; payloads are already redacted at envelope build time.
|
|
5
|
+
*
|
|
6
|
+
* Dependencies: same range loader pattern as verifyAuditHashChain.
|
|
7
|
+
*
|
|
8
|
+
* Test coverage: tests/integration/observability/explain-query-export.test.ts
|
|
9
|
+
*/
|
|
10
|
+
import * as crypto from "node:crypto";
|
|
11
|
+
function summarize(events) {
|
|
12
|
+
const ids = new Set();
|
|
13
|
+
for (const e of events) {
|
|
14
|
+
ids.add(e.redaction.manifestId);
|
|
15
|
+
}
|
|
16
|
+
return { eventCount: events.length, manifestIds: [...ids] };
|
|
17
|
+
}
|
|
18
|
+
export async function exportAuditBundle(range, deps) {
|
|
19
|
+
const events = [...(await deps.loadRange(range.from, range.to, range.families))].sort((a, b) => a.sequence - b.sequence);
|
|
20
|
+
return {
|
|
21
|
+
bundleId: crypto.randomUUID(),
|
|
22
|
+
generatedAt: new Date().toISOString(),
|
|
23
|
+
range,
|
|
24
|
+
events,
|
|
25
|
+
redactionSummary: summarize(events),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -26,7 +26,7 @@ export interface HeartbeatDecisionEvent {
|
|
|
26
26
|
traceId: string;
|
|
27
27
|
runtimeScope: "rhythm" | "user_task" | "user_reply";
|
|
28
28
|
triggerSource: "heartbeat_bridge" | "user_task" | "user_reply" | "interrupt" | "resume";
|
|
29
|
-
decisionStatus: "heartbeat_ok" | "intent_selected" | "denied" | "deferred";
|
|
29
|
+
decisionStatus: "heartbeat_ok" | "intent_selected" | "denied" | "deferred" | "runtime_carrier_only" | "delivery_unavailable";
|
|
30
30
|
reasons: string[];
|
|
31
31
|
intentId?: string;
|
|
32
32
|
mode: "active" | "quiet" | "maintenance_only" | "paused_for_interrupt";
|
|
@@ -15,6 +15,20 @@ export declare class GovernanceAudit {
|
|
|
15
15
|
private db;
|
|
16
16
|
constructor(db: ObservabilityDatabase);
|
|
17
17
|
recordAnchorChangeAudit(event: AnchorChangeAudit): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Generic governance-plane events (T5.1.2): fallback_written, effect_commit_advanced, connector_failure, etc.
|
|
20
|
+
* traceId is stored on target_asset_id for explain/trace correlation until a dedicated column exists.
|
|
21
|
+
*/
|
|
22
|
+
recordOperationalGovernanceEvent(input: {
|
|
23
|
+
id: string;
|
|
24
|
+
eventType: string;
|
|
25
|
+
traceId: string;
|
|
26
|
+
statusTo: string;
|
|
27
|
+
reason: string;
|
|
28
|
+
assetPath?: string;
|
|
29
|
+
supportingSources?: string[];
|
|
30
|
+
createdAt?: string;
|
|
31
|
+
}): Promise<void>;
|
|
18
32
|
recordCredentialLifecycle(event: CredentialLifecycleAudit): Promise<void>;
|
|
19
33
|
recordProposalApply(proposalId: string, targetAssetId: string, assetPath: string, beforeHash: string | undefined, afterHash: string | undefined, supportingSources: string[], reason: string): Promise<void>;
|
|
20
34
|
recordProposalReject(proposalId: string, targetAssetId: string, assetPath: string, reason: string): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { eq } from "drizzle-orm";
|
|
2
2
|
import { governanceAudit } from "../db/schema/index.js";
|
|
3
|
-
import { redactEvent } from "../redaction/manifest.js";
|
|
3
|
+
import { createEmptyManifest, redactEvent } from "../redaction/manifest.js";
|
|
4
4
|
import { persistRedactionManifest } from "./redaction-store.js";
|
|
5
5
|
export class GovernanceAudit {
|
|
6
6
|
db;
|
|
@@ -27,6 +27,30 @@ export class GovernanceAudit {
|
|
|
27
27
|
});
|
|
28
28
|
await persistRedactionManifest(this.db, redacted.id, "anchor.change", manifest);
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Generic governance-plane events (T5.1.2): fallback_written, effect_commit_advanced, connector_failure, etc.
|
|
32
|
+
* traceId is stored on target_asset_id for explain/trace correlation until a dedicated column exists.
|
|
33
|
+
*/
|
|
34
|
+
async recordOperationalGovernanceEvent(input) {
|
|
35
|
+
const createdAt = input.createdAt ?? new Date().toISOString();
|
|
36
|
+
await this.db.db.insert(governanceAudit).values({
|
|
37
|
+
id: input.id,
|
|
38
|
+
eventType: input.eventType,
|
|
39
|
+
proposalId: null,
|
|
40
|
+
targetAssetId: input.traceId,
|
|
41
|
+
assetPath: input.assetPath ?? null,
|
|
42
|
+
statusFrom: null,
|
|
43
|
+
statusTo: input.statusTo,
|
|
44
|
+
beforeHash: null,
|
|
45
|
+
afterHash: null,
|
|
46
|
+
supportingSources: JSON.stringify(input.supportingSources ?? []),
|
|
47
|
+
reason: input.reason,
|
|
48
|
+
verificationDeadline: null,
|
|
49
|
+
attemptsRemaining: null,
|
|
50
|
+
createdAt,
|
|
51
|
+
});
|
|
52
|
+
await persistRedactionManifest(this.db, input.id, input.eventType, createEmptyManifest());
|
|
53
|
+
}
|
|
30
54
|
async recordCredentialLifecycle(event) {
|
|
31
55
|
const { redacted, manifest } = redactEvent(event);
|
|
32
56
|
await this.db.db.insert(governanceAudit).values({
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T5.1.2 governance plane: connector attempts + state governance audit append ports.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: connector attempts map to executionAttempts telemetry; governance kinds map to
|
|
5
|
+
* governance_audit rows with traceId on target_asset_id for explain correlation.
|
|
6
|
+
*
|
|
7
|
+
* Test coverage: tests/unit/observability/governance-plane-recorder.test.ts
|
|
8
|
+
*/
|
|
9
|
+
import type { ObservabilityDatabase } from "../db/index.js";
|
|
10
|
+
import { ExecutionTelemetry } from "./execution-telemetry.js";
|
|
11
|
+
import { GovernanceAudit } from "./governance-audit.js";
|
|
12
|
+
export interface AuditAppendAck {
|
|
13
|
+
recordId: string;
|
|
14
|
+
appendedAt: string;
|
|
15
|
+
}
|
|
16
|
+
export type ConnectorAttemptOutcome = "started" | "succeeded" | "failed" | "sampled_telemetry";
|
|
17
|
+
export interface ConnectorAttemptAudit {
|
|
18
|
+
traceId: string;
|
|
19
|
+
decisionId: string;
|
|
20
|
+
intentId: string;
|
|
21
|
+
platformId: string;
|
|
22
|
+
capability: string;
|
|
23
|
+
channel: string;
|
|
24
|
+
outcome: ConnectorAttemptOutcome;
|
|
25
|
+
failureClass?: string;
|
|
26
|
+
idempotencyKey?: string;
|
|
27
|
+
metadata?: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
export type StateGovernanceKind = "fallback_written" | "effect_commit_advanced" | "connector_failure" | "anchor_proposal_received";
|
|
30
|
+
export interface StateGovernanceAudit {
|
|
31
|
+
id: string;
|
|
32
|
+
traceId: string;
|
|
33
|
+
kind: StateGovernanceKind;
|
|
34
|
+
reason: string;
|
|
35
|
+
decisionId?: string;
|
|
36
|
+
artifactPath?: string;
|
|
37
|
+
supportingSources?: string[];
|
|
38
|
+
createdAt?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare class GovernancePlaneRecorder {
|
|
41
|
+
private readonly telemetry;
|
|
42
|
+
private readonly governance;
|
|
43
|
+
constructor(telemetry: ExecutionTelemetry, governance: GovernanceAudit);
|
|
44
|
+
recordConnectorAttempt(audit: ConnectorAttemptAudit): Promise<AuditAppendAck>;
|
|
45
|
+
recordStateGovernance(event: StateGovernanceAudit): Promise<AuditAppendAck>;
|
|
46
|
+
}
|
|
47
|
+
export declare function createGovernancePlaneRecorder(db: ObservabilityDatabase): GovernancePlaneRecorder;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ExecutionTelemetry } from "./execution-telemetry.js";
|
|
2
|
+
import { GovernanceAudit } from "./governance-audit.js";
|
|
3
|
+
export class GovernancePlaneRecorder {
|
|
4
|
+
telemetry;
|
|
5
|
+
governance;
|
|
6
|
+
constructor(telemetry, governance) {
|
|
7
|
+
this.telemetry = telemetry;
|
|
8
|
+
this.governance = governance;
|
|
9
|
+
}
|
|
10
|
+
async recordConnectorAttempt(audit) {
|
|
11
|
+
const id = `ca-${audit.traceId}-${Date.now()}`;
|
|
12
|
+
const status = audit.outcome === "failed" ? "failed" : audit.outcome === "started" ? "started" : "succeeded";
|
|
13
|
+
const now = new Date().toISOString();
|
|
14
|
+
const attempt = {
|
|
15
|
+
id,
|
|
16
|
+
traceId: audit.traceId,
|
|
17
|
+
decisionId: audit.decisionId,
|
|
18
|
+
intentId: audit.intentId,
|
|
19
|
+
platformId: audit.platformId,
|
|
20
|
+
capability: audit.capability,
|
|
21
|
+
channel: audit.channel,
|
|
22
|
+
status,
|
|
23
|
+
failureClass: audit.failureClass,
|
|
24
|
+
idempotencyKey: audit.idempotencyKey,
|
|
25
|
+
metadata: {
|
|
26
|
+
...(audit.metadata ?? {}),
|
|
27
|
+
...(audit.outcome === "sampled_telemetry" ? { sampledTelemetry: true } : {}),
|
|
28
|
+
},
|
|
29
|
+
startedAt: now,
|
|
30
|
+
finishedAt: status === "started" ? undefined : now,
|
|
31
|
+
};
|
|
32
|
+
await this.telemetry.recordExecutionAttempt(attempt);
|
|
33
|
+
return { recordId: id, appendedAt: attempt.finishedAt ?? attempt.startedAt ?? now };
|
|
34
|
+
}
|
|
35
|
+
async recordStateGovernance(event) {
|
|
36
|
+
const createdAt = event.createdAt ?? new Date().toISOString();
|
|
37
|
+
const reason = event.decisionId !== undefined && event.decisionId.length > 0
|
|
38
|
+
? `${event.reason} decisionId=${event.decisionId}`
|
|
39
|
+
: event.reason;
|
|
40
|
+
await this.governance.recordOperationalGovernanceEvent({
|
|
41
|
+
id: event.id,
|
|
42
|
+
eventType: event.kind,
|
|
43
|
+
traceId: event.traceId,
|
|
44
|
+
statusTo: "recorded",
|
|
45
|
+
reason,
|
|
46
|
+
assetPath: event.artifactPath,
|
|
47
|
+
supportingSources: event.supportingSources,
|
|
48
|
+
createdAt,
|
|
49
|
+
});
|
|
50
|
+
return { recordId: event.id, appendedAt: createdAt };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function createGovernancePlaneRecorder(db) {
|
|
54
|
+
return new GovernancePlaneRecorder(new ExecutionTelemetry(db), new GovernanceAudit(db));
|
|
55
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { AppendOnlyAuditStore } from "../audit/append-only-audit-store.js";
|
|
2
|
+
import type { SourceRef } from "../../storage/life-evidence/types.js";
|
|
3
|
+
export type RuntimeScope = "rhythm" | "user_task" | "user_reply";
|
|
4
|
+
export type HeartbeatOutcome = "heartbeat_ok" | "intent_selected" | "denied" | "deferred" | "runtime_carrier_only" | "delivery_unavailable";
|
|
5
|
+
export type DeliveryAuditStatus = "not_requested" | "target_available" | "target_none" | "channel_missing" | "host_unsupported" | "ack_dropped" | "sent" | "failed" | "not_sent_fallback";
|
|
6
|
+
export type GroundingStatus = "pass" | "degraded" | "blocked";
|
|
7
|
+
export interface DecisionTracePayload {
|
|
8
|
+
decisionId: string;
|
|
9
|
+
traceId: string;
|
|
10
|
+
heartbeatId?: string;
|
|
11
|
+
runtimeScope: RuntimeScope;
|
|
12
|
+
outcome: HeartbeatOutcome;
|
|
13
|
+
selectedIntentId?: string;
|
|
14
|
+
candidateId?: string;
|
|
15
|
+
rhythmWindowKind?: string;
|
|
16
|
+
hardGuardVerdict?: "allow" | "deny" | "defer" | "silent";
|
|
17
|
+
outreachVerdict?: "allow" | "deny" | "defer";
|
|
18
|
+
deliveryAuditId?: string;
|
|
19
|
+
reasonCodes: string[];
|
|
20
|
+
sourceRefs: SourceRef[];
|
|
21
|
+
snapshotRef?: SourceRef;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
}
|
|
24
|
+
export interface DeliveryAuditPayload {
|
|
25
|
+
auditId: string;
|
|
26
|
+
decisionId: string;
|
|
27
|
+
traceId: string;
|
|
28
|
+
target?: "none" | "last" | "explicit";
|
|
29
|
+
channel?: string;
|
|
30
|
+
recipientRef?: string;
|
|
31
|
+
status: DeliveryAuditStatus;
|
|
32
|
+
messageId?: string;
|
|
33
|
+
hostProofRef?: SourceRef;
|
|
34
|
+
fallbackRef?: string;
|
|
35
|
+
ackDropMatched?: boolean;
|
|
36
|
+
hostVersion?: string;
|
|
37
|
+
reasonCodes: string[];
|
|
38
|
+
createdAt: string;
|
|
39
|
+
}
|
|
40
|
+
export interface SourceCoverageAuditPayload {
|
|
41
|
+
auditId: string;
|
|
42
|
+
traceId: string;
|
|
43
|
+
/** When set, explain index links this audit to the decision timeline. */
|
|
44
|
+
decisionId?: string;
|
|
45
|
+
subjectType: "quiet_artifact" | "outreach_draft" | "guidance_payload" | "decision_trace" | "host_report";
|
|
46
|
+
subjectRef: string;
|
|
47
|
+
usedSourceRefs: SourceRef[];
|
|
48
|
+
unresolvedRefs: SourceRef[];
|
|
49
|
+
coverageRatio: number;
|
|
50
|
+
unsupportedClaims: string[];
|
|
51
|
+
status: GroundingStatus;
|
|
52
|
+
reasonCodes: string[];
|
|
53
|
+
createdAt: string;
|
|
54
|
+
}
|
|
55
|
+
export interface GuidanceGroundingAuditPayload {
|
|
56
|
+
auditId: string;
|
|
57
|
+
traceId: string;
|
|
58
|
+
decisionId?: string;
|
|
59
|
+
requestId: string;
|
|
60
|
+
draftId?: string;
|
|
61
|
+
sceneType: "outreach" | "quiet_reflection" | "social" | "explain" | "user_reply_continuity" | "fallback_candidate";
|
|
62
|
+
groundingStatus: GroundingStatus;
|
|
63
|
+
usedSourceRefs: SourceRef[];
|
|
64
|
+
unsupportedClaims: string[];
|
|
65
|
+
guardViolations: string[];
|
|
66
|
+
deliveryWording?: "sendable" | "not_sent_fallback_candidate";
|
|
67
|
+
createdAt: string;
|
|
68
|
+
}
|
|
69
|
+
export interface ExplainLinkageSummary {
|
|
70
|
+
decisionId: string;
|
|
71
|
+
summary: string;
|
|
72
|
+
warnings: string[];
|
|
73
|
+
deliveryStatus?: DeliveryAuditStatus;
|
|
74
|
+
relatedEventIds: string[];
|
|
75
|
+
}
|
|
76
|
+
export declare class LivedExperienceAuditRecorder {
|
|
77
|
+
private readonly store;
|
|
78
|
+
private seq;
|
|
79
|
+
private readonly explainIndex;
|
|
80
|
+
constructor(store: AppendOnlyAuditStore);
|
|
81
|
+
private bumpSequence;
|
|
82
|
+
private touchDecision;
|
|
83
|
+
recordDecisionTrace(payload: DecisionTracePayload): {
|
|
84
|
+
eventId: string;
|
|
85
|
+
};
|
|
86
|
+
recordDeliveryAudit(payload: DeliveryAuditPayload): {
|
|
87
|
+
eventId: string;
|
|
88
|
+
};
|
|
89
|
+
recordSourceCoverage(payload: SourceCoverageAuditPayload): {
|
|
90
|
+
eventId: string;
|
|
91
|
+
};
|
|
92
|
+
recordGuidanceGrounding(payload: GuidanceGroundingAuditPayload): {
|
|
93
|
+
eventId: string;
|
|
94
|
+
};
|
|
95
|
+
explainLinkageForDecision(decisionId: string): ExplainLinkageSummary;
|
|
96
|
+
}
|
|
97
|
+
export declare function createLivedExperienceAuditRecorder(store?: AppendOnlyAuditStore): LivedExperienceAuditRecorder;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision trace, delivery audit, source coverage, guidance grounding + explain index (T5.2.1).
|
|
3
|
+
*
|
|
4
|
+
* Core logic: append-only envelopes with hash chain; explain index links decisionId to events and
|
|
5
|
+
* flags when delivery audit indicates no user-visible contact (target_none / not_sent_fallback).
|
|
6
|
+
* Test coverage: tests/unit/observability/lived-experience-audit.test.ts
|
|
7
|
+
*/
|
|
8
|
+
import * as crypto from "node:crypto";
|
|
9
|
+
import { AppendOnlyAuditStore } from "../audit/append-only-audit-store.js";
|
|
10
|
+
import { buildAuditEnvelope } from "../audit/audit-envelope.js";
|
|
11
|
+
function validateDecisionTrace(t) {
|
|
12
|
+
if (!t.decisionId?.trim())
|
|
13
|
+
throw new Error("decision_trace_requires_decision_id");
|
|
14
|
+
if (!t.traceId?.trim())
|
|
15
|
+
throw new Error("decision_trace_requires_trace_id");
|
|
16
|
+
if (!t.outcome)
|
|
17
|
+
throw new Error("decision_trace_requires_outcome");
|
|
18
|
+
}
|
|
19
|
+
function validateDeliveryAudit(a) {
|
|
20
|
+
if (!a.auditId?.trim())
|
|
21
|
+
throw new Error("delivery_audit_requires_audit_id");
|
|
22
|
+
if (!a.decisionId?.trim())
|
|
23
|
+
throw new Error("delivery_audit_requires_decision_id");
|
|
24
|
+
if (a.status === "sent") {
|
|
25
|
+
const ok = Boolean(a.messageId?.trim()) || Boolean(a.hostProofRef);
|
|
26
|
+
if (!ok)
|
|
27
|
+
throw new Error("delivery_audit_sent_requires_message_id_or_host_proof_ref");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class LivedExperienceAuditRecorder {
|
|
31
|
+
store;
|
|
32
|
+
seq = 0;
|
|
33
|
+
explainIndex = new Map();
|
|
34
|
+
constructor(store) {
|
|
35
|
+
this.store = store;
|
|
36
|
+
}
|
|
37
|
+
bumpSequence() {
|
|
38
|
+
this.seq += 1;
|
|
39
|
+
return this.seq;
|
|
40
|
+
}
|
|
41
|
+
touchDecision(decisionId, traceId, eventId) {
|
|
42
|
+
let e = this.explainIndex.get(decisionId);
|
|
43
|
+
if (!e) {
|
|
44
|
+
e = { traceIds: new Set(), eventIds: [], deliveryStatuses: [], fallbackRefs: [], noUserVisibleContact: false };
|
|
45
|
+
this.explainIndex.set(decisionId, e);
|
|
46
|
+
}
|
|
47
|
+
e.traceIds.add(traceId);
|
|
48
|
+
e.eventIds.push(eventId);
|
|
49
|
+
return e;
|
|
50
|
+
}
|
|
51
|
+
recordDecisionTrace(payload) {
|
|
52
|
+
validateDecisionTrace(payload);
|
|
53
|
+
const seq = this.bumpSequence();
|
|
54
|
+
const envelope = buildAuditEnvelope({
|
|
55
|
+
family: "heartbeat.decision",
|
|
56
|
+
plane: "decision",
|
|
57
|
+
traceId: payload.traceId,
|
|
58
|
+
sequence: seq,
|
|
59
|
+
payload,
|
|
60
|
+
previousHash: this.store.lastRecordHash(),
|
|
61
|
+
eventId: crypto.randomUUID(),
|
|
62
|
+
createdAt: payload.createdAt,
|
|
63
|
+
});
|
|
64
|
+
this.store.append(envelope);
|
|
65
|
+
const entry = this.touchDecision(payload.decisionId, payload.traceId, envelope.eventId);
|
|
66
|
+
if (payload.outcome === "heartbeat_ok" &&
|
|
67
|
+
payload.reasonCodes.some((c) => c.includes("target_none") || c === "target_none")) {
|
|
68
|
+
entry.noUserVisibleContact = true;
|
|
69
|
+
}
|
|
70
|
+
return { eventId: envelope.eventId };
|
|
71
|
+
}
|
|
72
|
+
recordDeliveryAudit(payload) {
|
|
73
|
+
validateDeliveryAudit(payload);
|
|
74
|
+
const seq = this.bumpSequence();
|
|
75
|
+
const envelope = buildAuditEnvelope({
|
|
76
|
+
family: "delivery",
|
|
77
|
+
plane: "delivery",
|
|
78
|
+
traceId: payload.traceId,
|
|
79
|
+
sequence: seq,
|
|
80
|
+
payload,
|
|
81
|
+
previousHash: this.store.lastRecordHash(),
|
|
82
|
+
eventId: payload.auditId,
|
|
83
|
+
createdAt: payload.createdAt,
|
|
84
|
+
});
|
|
85
|
+
this.store.append(envelope);
|
|
86
|
+
const entry = this.touchDecision(payload.decisionId, payload.traceId, envelope.eventId);
|
|
87
|
+
entry.deliveryStatuses.push(payload.status);
|
|
88
|
+
if (payload.fallbackRef)
|
|
89
|
+
entry.fallbackRefs.push(payload.fallbackRef);
|
|
90
|
+
if (payload.status === "target_none" ||
|
|
91
|
+
payload.status === "not_sent_fallback" ||
|
|
92
|
+
payload.status === "channel_missing" ||
|
|
93
|
+
payload.status === "host_unsupported" ||
|
|
94
|
+
payload.status === "failed" ||
|
|
95
|
+
payload.status === "ack_dropped") {
|
|
96
|
+
entry.noUserVisibleContact = true;
|
|
97
|
+
}
|
|
98
|
+
return { eventId: envelope.eventId };
|
|
99
|
+
}
|
|
100
|
+
recordSourceCoverage(payload) {
|
|
101
|
+
const seq = this.bumpSequence();
|
|
102
|
+
const envelope = buildAuditEnvelope({
|
|
103
|
+
family: "source_coverage",
|
|
104
|
+
plane: "source_coverage",
|
|
105
|
+
traceId: payload.traceId,
|
|
106
|
+
sequence: seq,
|
|
107
|
+
payload,
|
|
108
|
+
previousHash: this.store.lastRecordHash(),
|
|
109
|
+
eventId: payload.auditId,
|
|
110
|
+
createdAt: payload.createdAt,
|
|
111
|
+
});
|
|
112
|
+
this.store.append(envelope);
|
|
113
|
+
if (payload.decisionId) {
|
|
114
|
+
this.touchDecision(payload.decisionId, payload.traceId, envelope.eventId);
|
|
115
|
+
}
|
|
116
|
+
return { eventId: envelope.eventId };
|
|
117
|
+
}
|
|
118
|
+
recordGuidanceGrounding(payload) {
|
|
119
|
+
const seq = this.bumpSequence();
|
|
120
|
+
const envelope = buildAuditEnvelope({
|
|
121
|
+
family: "guidance.grounding",
|
|
122
|
+
plane: "source_coverage",
|
|
123
|
+
traceId: payload.traceId,
|
|
124
|
+
sequence: seq,
|
|
125
|
+
payload,
|
|
126
|
+
previousHash: this.store.lastRecordHash(),
|
|
127
|
+
eventId: payload.auditId,
|
|
128
|
+
createdAt: payload.createdAt,
|
|
129
|
+
});
|
|
130
|
+
this.store.append(envelope);
|
|
131
|
+
if (payload.decisionId) {
|
|
132
|
+
this.touchDecision(payload.decisionId, payload.traceId, envelope.eventId);
|
|
133
|
+
}
|
|
134
|
+
return { eventId: envelope.eventId };
|
|
135
|
+
}
|
|
136
|
+
explainLinkageForDecision(decisionId) {
|
|
137
|
+
const entry = this.explainIndex.get(decisionId);
|
|
138
|
+
const warnings = [];
|
|
139
|
+
if (!entry) {
|
|
140
|
+
return {
|
|
141
|
+
decisionId,
|
|
142
|
+
summary: "no_audit_events_indexed_for_decision",
|
|
143
|
+
warnings: ["no_indexed_events"],
|
|
144
|
+
relatedEventIds: [],
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (entry.noUserVisibleContact) {
|
|
148
|
+
warnings.push("no_user_visible_contact_claim_prohibited");
|
|
149
|
+
}
|
|
150
|
+
const lastDelivery = entry.deliveryStatuses[entry.deliveryStatuses.length - 1];
|
|
151
|
+
return {
|
|
152
|
+
decisionId,
|
|
153
|
+
summary: `indexed_events=${entry.eventIds.length};delivery=${lastDelivery ?? "unknown"}`,
|
|
154
|
+
warnings,
|
|
155
|
+
deliveryStatus: lastDelivery,
|
|
156
|
+
relatedEventIds: [...entry.eventIds],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export function createLivedExperienceAuditRecorder(store) {
|
|
161
|
+
return new LivedExperienceAuditRecorder(store ?? new AppendOnlyAuditStore());
|
|
162
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional better-sqlite3 load probe (T4.1.4). Second Nature state DB currently uses sql.js only;
|
|
3
|
+
* this probe records whether the native module can load for packaging / host diagnostics.
|
|
4
|
+
*/
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
export function probeNativeSqliteLoad() {
|
|
8
|
+
try {
|
|
9
|
+
const Database = require("better-sqlite3");
|
|
10
|
+
const db = new Database(":memory:");
|
|
11
|
+
db.close();
|
|
12
|
+
let version;
|
|
13
|
+
try {
|
|
14
|
+
const pkg = require("better-sqlite3/package.json");
|
|
15
|
+
version = pkg.version;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
version = undefined;
|
|
19
|
+
}
|
|
20
|
+
return { moduleLoadOk: true, version };
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return {
|
|
24
|
+
moduleLoadOk: false,
|
|
25
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StateDatabase } from "../db/index.js";
|
|
2
|
+
import { createRepairAndBackupService, type RepairAndBackupOptions } from "../services/repair-and-backup.js";
|
|
3
|
+
export interface RepairStateIndexesOptions {
|
|
4
|
+
startupGate?: boolean;
|
|
5
|
+
workspaceRoot: string;
|
|
6
|
+
/** When true, also runs asset registry repair + backup (existing repair service) */
|
|
7
|
+
reconcileAssets?: boolean;
|
|
8
|
+
assetRepairOptions?: RepairAndBackupOptions;
|
|
9
|
+
}
|
|
10
|
+
export type RepairGateStatus = "ok" | "repair_required";
|
|
11
|
+
export interface RepairSummary {
|
|
12
|
+
status: RepairGateStatus;
|
|
13
|
+
repairedEvidenceIndexRows: number;
|
|
14
|
+
repairNotes: string[];
|
|
15
|
+
assetRepair?: Awaited<ReturnType<ReturnType<typeof createRepairAndBackupService>["runStartupRepair"]>>;
|
|
16
|
+
}
|
|
17
|
+
export declare function repairStateIndexes(state: StateDatabase, options: RepairStateIndexesOptions): Promise<RepairSummary>;
|