@haaaiawd/second-nature 0.1.18 → 0.1.20

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.
Files changed (190) hide show
  1. package/index.js +911 -855
  2. package/openclaw.plugin.json +29 -29
  3. package/package.json +52 -52
  4. package/runtime/cli/commands/index.d.ts +14 -14
  5. package/runtime/cli/commands/index.js +224 -193
  6. package/runtime/cli/explain/explain-surface-subject.d.ts +8 -8
  7. package/runtime/cli/explain/explain-surface-subject.js +9 -9
  8. package/runtime/cli/explain/format-explanation.d.ts +12 -12
  9. package/runtime/cli/explain/format-explanation.js +12 -12
  10. package/runtime/cli/explain/resolve-subject.js +41 -41
  11. package/runtime/cli/host-capability/classify-delivery.d.ts +14 -14
  12. package/runtime/cli/host-capability/classify-delivery.js +20 -20
  13. package/runtime/cli/host-capability/probe-host-capability.d.ts +2 -2
  14. package/runtime/cli/host-capability/probe-host-capability.js +58 -58
  15. package/runtime/cli/host-capability/record-host-capability.d.ts +6 -6
  16. package/runtime/cli/host-capability/record-host-capability.js +14 -14
  17. package/runtime/cli/host-capability/types.d.ts +71 -71
  18. package/runtime/cli/host-capability/types.js +6 -6
  19. package/runtime/cli/host-smoke/run-host-smoke.d.ts +2 -2
  20. package/runtime/cli/host-smoke/run-host-smoke.js +40 -40
  21. package/runtime/cli/host-smoke/types.d.ts +35 -35
  22. package/runtime/cli/host-smoke/types.js +6 -6
  23. package/runtime/cli/index.js +67 -58
  24. package/runtime/cli/ops/heartbeat-surface.d.ts +45 -38
  25. package/runtime/cli/ops/heartbeat-surface.js +79 -73
  26. package/runtime/cli/ops/ops-router.d.ts +32 -19
  27. package/runtime/cli/ops/ops-router.js +188 -89
  28. package/runtime/cli/ops/show-operator-fallback.d.ts +13 -13
  29. package/runtime/cli/ops/show-operator-fallback.js +22 -22
  30. package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +40 -19
  31. package/runtime/cli/ops/workspace-heartbeat-runner.js +93 -39
  32. package/runtime/cli/read-models/index.d.ts +46 -29
  33. package/runtime/cli/read-models/index.js +391 -256
  34. package/runtime/cli/read-models/operator-explain-map.d.ts +6 -6
  35. package/runtime/cli/read-models/operator-explain-map.js +10 -10
  36. package/runtime/cli/read-models/types.d.ts +129 -79
  37. package/runtime/cli/runtime/runtime-artifact-boundary.d.ts +28 -28
  38. package/runtime/cli/runtime/runtime-artifact-boundary.js +94 -94
  39. package/runtime/connectors/base/contract.d.ts +87 -87
  40. package/runtime/connectors/base/execution-policy.d.ts +47 -47
  41. package/runtime/connectors/base/execution-policy.js +82 -82
  42. package/runtime/connectors/base/index.d.ts +8 -8
  43. package/runtime/connectors/base/index.js +8 -8
  44. package/runtime/connectors/base/manifest.d.ts +64 -64
  45. package/runtime/connectors/base/manifest.js +86 -86
  46. package/runtime/connectors/base/map-life-evidence.d.ts +16 -16
  47. package/runtime/connectors/base/map-life-evidence.js +79 -79
  48. package/runtime/connectors/base/policy-layer.d.ts +29 -29
  49. package/runtime/connectors/base/policy-layer.js +198 -198
  50. package/runtime/connectors/base/route-planner.js +99 -99
  51. package/runtime/connectors/index.d.ts +5 -5
  52. package/runtime/connectors/index.js +5 -5
  53. package/runtime/connectors/near-real/near-real-connector-smoke.d.ts +19 -19
  54. package/runtime/connectors/near-real/near-real-connector-smoke.js +152 -152
  55. package/runtime/core/second-nature/heartbeat/heartbeat-executor.js +114 -114
  56. package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +63 -63
  57. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +162 -139
  58. package/runtime/core/second-nature/heartbeat/index.d.ts +8 -8
  59. package/runtime/core/second-nature/heartbeat/index.js +7 -7
  60. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.d.ts +21 -21
  61. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.js +35 -35
  62. package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +28 -28
  63. package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +35 -35
  64. package/runtime/core/second-nature/heartbeat/signal.d.ts +42 -42
  65. package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +51 -51
  66. package/runtime/core/second-nature/index.d.ts +22 -22
  67. package/runtime/core/second-nature/index.js +22 -22
  68. package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +100 -100
  69. package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +144 -144
  70. package/runtime/core/second-nature/orchestrator/guard-layer.d.ts +8 -8
  71. package/runtime/core/second-nature/orchestrator/guard-layer.js +110 -110
  72. package/runtime/core/second-nature/orchestrator/intent-planner.d.ts +13 -13
  73. package/runtime/core/second-nature/orchestrator/intent-planner.js +199 -199
  74. package/runtime/core/second-nature/orchestrator/lease-manager.d.ts +14 -14
  75. package/runtime/core/second-nature/orchestrator/lease-manager.js +58 -58
  76. package/runtime/core/second-nature/outreach/build-outreach-draft-request.d.ts +6 -6
  77. package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +63 -63
  78. package/runtime/core/second-nature/outreach/delivery-target.d.ts +26 -26
  79. package/runtime/core/second-nature/outreach/delivery-target.js +70 -70
  80. package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +38 -38
  81. package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +119 -119
  82. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.d.ts +7 -7
  83. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +45 -45
  84. package/runtime/core/second-nature/outreach/judge-outreach.d.ts +40 -40
  85. package/runtime/core/second-nature/outreach/judge-outreach.js +121 -121
  86. package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +21 -21
  87. package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +123 -123
  88. package/runtime/core/second-nature/rhythm/planner-rhythm-window.d.ts +15 -15
  89. package/runtime/core/second-nature/rhythm/planner-rhythm-window.js +52 -52
  90. package/runtime/core/second-nature/rhythm/policy-bridge.d.ts +19 -19
  91. package/runtime/core/second-nature/rhythm/policy-bridge.js +34 -34
  92. package/runtime/core/second-nature/runtime/service-entry.js +45 -45
  93. package/runtime/core/second-nature/types.d.ts +51 -51
  94. package/runtime/guidance/draft-outreach-message.d.ts +7 -7
  95. package/runtime/guidance/draft-outreach-message.js +42 -42
  96. package/runtime/guidance/evidence-guidance.d.ts +40 -40
  97. package/runtime/guidance/evidence-guidance.js +52 -52
  98. package/runtime/guidance/index.d.ts +11 -11
  99. package/runtime/guidance/index.js +11 -11
  100. package/runtime/guidance/outreach-draft-schema.d.ts +228 -228
  101. package/runtime/guidance/outreach-draft-schema.js +80 -80
  102. package/runtime/observability/audit/append-only-audit-store.d.ts +14 -14
  103. package/runtime/observability/audit/append-only-audit-store.js +21 -21
  104. package/runtime/observability/audit/audit-envelope.d.ts +51 -51
  105. package/runtime/observability/audit/audit-envelope.js +130 -130
  106. package/runtime/observability/audit/verify-audit-hash-chain.d.ts +23 -23
  107. package/runtime/observability/audit/verify-audit-hash-chain.js +83 -83
  108. package/runtime/observability/db/index.js +47 -47
  109. package/runtime/observability/db/schema/host-capability-reports.d.ts +180 -180
  110. package/runtime/observability/db/schema/host-capability-reports.js +12 -12
  111. package/runtime/observability/db/schema/index.d.ts +947 -947
  112. package/runtime/observability/db/schema/index.js +71 -71
  113. package/runtime/observability/index.d.ts +20 -20
  114. package/runtime/observability/index.js +19 -19
  115. package/runtime/observability/query/explain-query.d.ts +48 -48
  116. package/runtime/observability/query/explain-query.js +114 -114
  117. package/runtime/observability/query/export-audit-bundle.d.ts +22 -22
  118. package/runtime/observability/query/export-audit-bundle.js +27 -27
  119. package/runtime/observability/services/decision-ledger.d.ts +46 -46
  120. package/runtime/observability/services/decision-ledger.js +161 -161
  121. package/runtime/observability/services/governance-audit.d.ts +41 -41
  122. package/runtime/observability/services/governance-audit.js +163 -163
  123. package/runtime/observability/services/governance-plane-recorder.d.ts +47 -47
  124. package/runtime/observability/services/governance-plane-recorder.js +55 -55
  125. package/runtime/observability/services/lived-experience-audit.d.ts +97 -97
  126. package/runtime/observability/services/lived-experience-audit.js +162 -162
  127. package/runtime/observability/services/observability-retention.d.ts +10 -0
  128. package/runtime/observability/services/observability-retention.js +37 -0
  129. package/runtime/observability/services/runtime-decision-recorder.d.ts +29 -29
  130. package/runtime/observability/services/runtime-decision-recorder.js +94 -94
  131. package/runtime/storage/bootstrap/native-sqlite-probe.d.ts +7 -7
  132. package/runtime/storage/bootstrap/native-sqlite-probe.js +28 -28
  133. package/runtime/storage/bootstrap/repair-gate.d.ts +17 -17
  134. package/runtime/storage/bootstrap/repair-gate.js +71 -71
  135. package/runtime/storage/bootstrap/storage-mode-smoke.d.ts +38 -38
  136. package/runtime/storage/bootstrap/storage-mode-smoke.js +85 -85
  137. package/runtime/storage/db/index.js +61 -61
  138. package/runtime/storage/db/schema/delivery-attempts.d.ts +199 -199
  139. package/runtime/storage/db/schema/delivery-attempts.js +13 -13
  140. package/runtime/storage/db/schema/index.d.ts +9 -9
  141. package/runtime/storage/db/schema/index.js +9 -9
  142. package/runtime/storage/db/schema/life-evidence-index.d.ts +161 -161
  143. package/runtime/storage/db/schema/life-evidence-index.js +11 -11
  144. package/runtime/storage/db/schema/operator-fallback-artifacts.d.ts +161 -161
  145. package/runtime/storage/db/schema/operator-fallback-artifacts.js +11 -11
  146. package/runtime/storage/db/schema/policies.d.ts +98 -98
  147. package/runtime/storage/db/schema/policies.js +8 -8
  148. package/runtime/storage/delivery/query-delivery-attempts.d.ts +3 -3
  149. package/runtime/storage/delivery/query-delivery-attempts.js +32 -32
  150. package/runtime/storage/delivery/types.d.ts +27 -27
  151. package/runtime/storage/delivery/types.js +1 -1
  152. package/runtime/storage/delivery/write-delivery-attempt.d.ts +6 -6
  153. package/runtime/storage/delivery/write-delivery-attempt.js +36 -36
  154. package/runtime/storage/fallback/load-operator-fallback.d.ts +14 -14
  155. package/runtime/storage/fallback/load-operator-fallback.js +47 -47
  156. package/runtime/storage/fallback/operator-fallback-types.d.ts +9 -9
  157. package/runtime/storage/fallback/operator-fallback-types.js +1 -1
  158. package/runtime/storage/fallback/operator-fallback-view.d.ts +11 -11
  159. package/runtime/storage/fallback/operator-fallback-view.js +1 -1
  160. package/runtime/storage/fallback/write-operator-fallback.d.ts +6 -6
  161. package/runtime/storage/fallback/write-operator-fallback.js +21 -21
  162. package/runtime/storage/index.d.ts +37 -37
  163. package/runtime/storage/index.js +30 -30
  164. package/runtime/storage/life-evidence/append-life-evidence.d.ts +7 -7
  165. package/runtime/storage/life-evidence/append-life-evidence.js +64 -64
  166. package/runtime/storage/life-evidence/types.d.ts +45 -45
  167. package/runtime/storage/life-evidence/types.js +6 -6
  168. package/runtime/storage/quiet/persist-quiet-artifact.d.ts +7 -7
  169. package/runtime/storage/quiet/persist-quiet-artifact.js +22 -22
  170. package/runtime/storage/quiet/quiet-artifact-types.d.ts +18 -18
  171. package/runtime/storage/quiet/quiet-artifact-types.js +1 -1
  172. package/runtime/storage/quiet/quiet-artifact-writer.d.ts +15 -15
  173. package/runtime/storage/quiet/quiet-artifact-writer.js +56 -56
  174. package/runtime/storage/repositories/credential-repository.js +30 -30
  175. package/runtime/storage/rhythm/rhythm-policy-snapshot.d.ts +10 -10
  176. package/runtime/storage/rhythm/rhythm-policy-snapshot.js +34 -34
  177. package/runtime/storage/services/credential-vault.d.ts +13 -13
  178. package/runtime/storage/services/credential-vault.js +116 -116
  179. package/runtime/storage/snapshots/continuity-snapshot.d.ts +9 -9
  180. package/runtime/storage/snapshots/continuity-snapshot.js +41 -41
  181. package/runtime/storage/snapshots/life-evidence-snapshot.d.ts +6 -6
  182. package/runtime/storage/snapshots/life-evidence-snapshot.js +114 -114
  183. package/runtime/storage/snapshots/types.d.ts +58 -58
  184. package/runtime/storage/snapshots/types.js +1 -1
  185. package/runtime/storage/state-api.js +104 -104
  186. package/runtime/storage/user-interest/load-user-interest-snapshot.d.ts +2 -2
  187. package/runtime/storage/user-interest/load-user-interest-snapshot.js +150 -150
  188. package/runtime/storage/user-interest/types.d.ts +25 -25
  189. package/runtime/storage/user-interest/types.js +1 -1
  190. package/workspace-ops-bridge.js +90 -81
@@ -1,97 +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;
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;
@@ -1,162 +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
- }
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,10 @@
1
+ import type { ObservabilityDatabase } from "../db/index.js";
2
+ export interface RetentionCleanupInput {
3
+ /** Delete rows with createdAt < this ISO string. */
4
+ beforeDate: string;
5
+ }
6
+ export interface RetentionCleanupResult {
7
+ decisionLedgerDeleted: number;
8
+ executionAttemptsDeleted: number;
9
+ }
10
+ export declare function pruneObservabilityTables(db: ObservabilityDatabase, input: RetentionCleanupInput): Promise<RetentionCleanupResult>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Observability retention cleanup (P2-06).
3
+ *
4
+ * Core logic: delete rows older than a retention threshold from
5
+ * decision_ledger and execution_attempts. Host capability reports and
6
+ * governance audit are intentionally kept longer (they are rare and
7
+ * operator-relevant).
8
+ *
9
+ * Boundaries:
10
+ * - Does NOT vacuum the SQLite file (callers may do so separately).
11
+ * - Returns honest counts so operators can verify.
12
+ * - Safe to run while the system is active (SQLite DELETE is row-level).
13
+ */
14
+ import { lt, sql } from "drizzle-orm";
15
+ import { decisionLedger, executionAttempts } from "../db/schema/index.js";
16
+ export async function pruneObservabilityTables(db, input) {
17
+ // Count before delete so we can return honest deletion numbers
18
+ // (SQLite DELETE result does not expose changes in Drizzle's type).
19
+ const dlBefore = await db.db
20
+ .select({ count: sql `count(*)` })
21
+ .from(decisionLedger)
22
+ .where(lt(decisionLedger.createdAt, input.beforeDate));
23
+ const eaBefore = await db.db
24
+ .select({ count: sql `count(*)` })
25
+ .from(executionAttempts)
26
+ .where(lt(executionAttempts.startedAt, input.beforeDate));
27
+ await db.db
28
+ .delete(decisionLedger)
29
+ .where(lt(decisionLedger.createdAt, input.beforeDate));
30
+ await db.db
31
+ .delete(executionAttempts)
32
+ .where(lt(executionAttempts.startedAt, input.beforeDate));
33
+ return {
34
+ decisionLedgerDeleted: dlBefore[0]?.count ?? 0,
35
+ executionAttemptsDeleted: eaBefore[0]?.count ?? 0,
36
+ };
37
+ }
@@ -1,29 +1,29 @@
1
- import type { ObservabilityDatabase } from "../db/index.js";
2
- import { DecisionLedger, type HeartbeatDecisionEvent } from "./decision-ledger.js";
3
- import { ExecutionTelemetry } from "./execution-telemetry.js";
4
- import type { HeartbeatCycleResult, HeartbeatSignal } from "../../core/second-nature/heartbeat/signal.js";
5
- export declare const RUNTIME_DECISION_TRACE_PREFIX = "sn-runtime-";
6
- export declare const RUNTIME_INTERNAL_PLATFORM_ID = "second-nature-runtime";
7
- export interface RecordHeartbeatCycleInput {
8
- cycle: HeartbeatCycleResult;
9
- signal: HeartbeatSignal;
10
- /**
11
- * Override rhythm `mode` written to the ledger row. When omitted, falls back
12
- * to `"active"`; downstream loadStatus only treats `quiet` /
13
- * `maintenance_only` / `paused_for_interrupt` as Quiet-aware values.
14
- */
15
- rhythmMode?: HeartbeatDecisionEvent["mode"];
16
- }
17
- export interface RecordHeartbeatCycleOutput {
18
- traceId: string;
19
- decisionId: string;
20
- attemptId: string;
21
- }
22
- export interface RuntimeDecisionRecorder {
23
- recordHeartbeatCycle(input: RecordHeartbeatCycleInput): Promise<RecordHeartbeatCycleOutput>;
24
- }
25
- export interface CreateRuntimeDecisionRecorderDeps {
26
- ledger?: DecisionLedger;
27
- telemetry?: ExecutionTelemetry;
28
- }
29
- export declare function createRuntimeDecisionRecorder(observabilityDb: ObservabilityDatabase, overrides?: CreateRuntimeDecisionRecorderDeps): RuntimeDecisionRecorder;
1
+ import type { ObservabilityDatabase } from "../db/index.js";
2
+ import { DecisionLedger, type HeartbeatDecisionEvent } from "./decision-ledger.js";
3
+ import { ExecutionTelemetry } from "./execution-telemetry.js";
4
+ import type { HeartbeatCycleResult, HeartbeatSignal } from "../../core/second-nature/heartbeat/signal.js";
5
+ export declare const RUNTIME_DECISION_TRACE_PREFIX = "sn-runtime-";
6
+ export declare const RUNTIME_INTERNAL_PLATFORM_ID = "second-nature-runtime";
7
+ export interface RecordHeartbeatCycleInput {
8
+ cycle: HeartbeatCycleResult;
9
+ signal: HeartbeatSignal;
10
+ /**
11
+ * Override rhythm `mode` written to the ledger row. When omitted, falls back
12
+ * to `"active"`; downstream loadStatus only treats `quiet` /
13
+ * `maintenance_only` / `paused_for_interrupt` as Quiet-aware values.
14
+ */
15
+ rhythmMode?: HeartbeatDecisionEvent["mode"];
16
+ }
17
+ export interface RecordHeartbeatCycleOutput {
18
+ traceId: string;
19
+ decisionId: string;
20
+ attemptId: string;
21
+ }
22
+ export interface RuntimeDecisionRecorder {
23
+ recordHeartbeatCycle(input: RecordHeartbeatCycleInput): Promise<RecordHeartbeatCycleOutput>;
24
+ }
25
+ export interface CreateRuntimeDecisionRecorderDeps {
26
+ ledger?: DecisionLedger;
27
+ telemetry?: ExecutionTelemetry;
28
+ }
29
+ export declare function createRuntimeDecisionRecorder(observabilityDb: ObservabilityDatabase, overrides?: CreateRuntimeDecisionRecorderDeps): RuntimeDecisionRecorder;