@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,21 +1,21 @@
1
- export class AppendOnlyAuditStore {
2
- events = [];
3
- append(envelope) {
4
- const last = this.events[this.events.length - 1];
5
- if (last) {
6
- if (envelope.integrity.previousHash !== last.integrity.recordHash) {
7
- throw new Error("audit_previous_hash_mismatch");
8
- }
9
- }
10
- else if (envelope.integrity.previousHash !== undefined) {
11
- throw new Error("audit_genesis_previous_hash");
12
- }
13
- this.events.push(envelope);
14
- }
15
- list() {
16
- return this.events;
17
- }
18
- lastRecordHash() {
19
- return this.events[this.events.length - 1]?.integrity.recordHash;
20
- }
21
- }
1
+ export class AppendOnlyAuditStore {
2
+ events = [];
3
+ append(envelope) {
4
+ const last = this.events[this.events.length - 1];
5
+ if (last) {
6
+ if (envelope.integrity.previousHash !== last.integrity.recordHash) {
7
+ throw new Error("audit_previous_hash_mismatch");
8
+ }
9
+ }
10
+ else if (envelope.integrity.previousHash !== undefined) {
11
+ throw new Error("audit_genesis_previous_hash");
12
+ }
13
+ this.events.push(envelope);
14
+ }
15
+ list() {
16
+ return this.events;
17
+ }
18
+ lastRecordHash() {
19
+ return this.events[this.events.length - 1]?.integrity.recordHash;
20
+ }
21
+ }
@@ -1,51 +1,51 @@
1
- import { type RedactionManifest as FieldRedactionManifest } from "../redaction/manifest.js";
2
- export type AuditPlane = "decision" | "delivery" | "source_coverage" | "governance" | "telemetry";
3
- export type AuditEventFamily = "heartbeat.decision" | "delivery" | "source_coverage" | "guidance.grounding" | "host_capability" | "connector.attempt" | "state.governance";
4
- export type AuditEnvelopeSensitivity = "public" | "internal" | "private" | "credential" | "sensitive";
5
- export interface AuditRedactionManifest {
6
- manifestId: string;
7
- maskedPaths: string[];
8
- erasedPaths: string[];
9
- hashedPaths: string[];
10
- contentRefPaths: string[];
11
- sensitivity: AuditEnvelopeSensitivity;
12
- }
13
- export interface AuditIntegrity {
14
- previousHash?: string;
15
- recordHash: string;
16
- schemaVersion: "observability.v5";
17
- }
18
- export interface AuditEnvelope<TPayload> {
19
- eventId: string;
20
- family: AuditEventFamily;
21
- plane: AuditPlane;
22
- traceId: string;
23
- sequence: number;
24
- createdAt: string;
25
- payload: TPayload;
26
- redaction: AuditRedactionManifest;
27
- integrity: AuditIntegrity;
28
- }
29
- export interface RedactAuditEventResult<TPayload> {
30
- payload: TPayload;
31
- redaction: AuditRedactionManifest;
32
- }
33
- /**
34
- * Apply field redaction rules and lift manifests to audit path vocabulary (observability-system.detail §2).
35
- */
36
- export declare function redactAuditEvent<TPayload extends object>(payload: TPayload): RedactAuditEventResult<TPayload>;
37
- export interface BuildAuditEnvelopeInput<TPayload extends object> {
38
- family: AuditEventFamily;
39
- plane: AuditPlane;
40
- traceId: string;
41
- sequence: number;
42
- payload: TPayload;
43
- previousHash?: string;
44
- eventId?: string;
45
- createdAt?: string;
46
- }
47
- /** Recompute integrity hash for an existing envelope (T5.2.2 / verifyAuditHashChain). */
48
- export declare function computeAuditRecordHash(envelope: AuditEnvelope<unknown>): string;
49
- export declare function buildAuditEnvelope<TPayload extends object>(input: BuildAuditEnvelopeInput<TPayload>): AuditEnvelope<TPayload>;
50
- /** @internal Maps legacy field manifest to audit manifest for persistence helpers. */
51
- export declare function auditManifestFromFieldManifest(manifest: FieldRedactionManifest): AuditRedactionManifest;
1
+ import { type RedactionManifest as FieldRedactionManifest } from "../redaction/manifest.js";
2
+ export type AuditPlane = "decision" | "delivery" | "source_coverage" | "governance" | "telemetry";
3
+ export type AuditEventFamily = "heartbeat.decision" | "delivery" | "source_coverage" | "guidance.grounding" | "host_capability" | "connector.attempt" | "state.governance";
4
+ export type AuditEnvelopeSensitivity = "public" | "internal" | "private" | "credential" | "sensitive";
5
+ export interface AuditRedactionManifest {
6
+ manifestId: string;
7
+ maskedPaths: string[];
8
+ erasedPaths: string[];
9
+ hashedPaths: string[];
10
+ contentRefPaths: string[];
11
+ sensitivity: AuditEnvelopeSensitivity;
12
+ }
13
+ export interface AuditIntegrity {
14
+ previousHash?: string;
15
+ recordHash: string;
16
+ schemaVersion: "observability.v5";
17
+ }
18
+ export interface AuditEnvelope<TPayload> {
19
+ eventId: string;
20
+ family: AuditEventFamily;
21
+ plane: AuditPlane;
22
+ traceId: string;
23
+ sequence: number;
24
+ createdAt: string;
25
+ payload: TPayload;
26
+ redaction: AuditRedactionManifest;
27
+ integrity: AuditIntegrity;
28
+ }
29
+ export interface RedactAuditEventResult<TPayload> {
30
+ payload: TPayload;
31
+ redaction: AuditRedactionManifest;
32
+ }
33
+ /**
34
+ * Apply field redaction rules and lift manifests to audit path vocabulary (observability-system.detail §2).
35
+ */
36
+ export declare function redactAuditEvent<TPayload extends object>(payload: TPayload): RedactAuditEventResult<TPayload>;
37
+ export interface BuildAuditEnvelopeInput<TPayload extends object> {
38
+ family: AuditEventFamily;
39
+ plane: AuditPlane;
40
+ traceId: string;
41
+ sequence: number;
42
+ payload: TPayload;
43
+ previousHash?: string;
44
+ eventId?: string;
45
+ createdAt?: string;
46
+ }
47
+ /** Recompute integrity hash for an existing envelope (T5.2.2 / verifyAuditHashChain). */
48
+ export declare function computeAuditRecordHash(envelope: AuditEnvelope<unknown>): string;
49
+ export declare function buildAuditEnvelope<TPayload extends object>(input: BuildAuditEnvelopeInput<TPayload>): AuditEnvelope<TPayload>;
50
+ /** @internal Maps legacy field manifest to audit manifest for persistence helpers. */
51
+ export declare function auditManifestFromFieldManifest(manifest: FieldRedactionManifest): AuditRedactionManifest;
@@ -1,130 +1,130 @@
1
- /**
2
- * Audit envelope construction + redaction for observability-system v5.
3
- *
4
- * Core logic: redact structured payloads, attach RedactionManifest (L0/L1 contract paths),
5
- * compute hash-chain fields for append-only ledger rows.
6
- *
7
- * Dependencies: observability redactEvent (field-level); maps to envelope redaction paths.
8
- *
9
- * Boundaries: persistence is delegated to AppendOnlyAuditStore / DB adapters.
10
- *
11
- * Test coverage: tests/unit/observability/audit-envelope.test.ts
12
- */
13
- import * as crypto from "node:crypto";
14
- import { redactEvent } from "../redaction/manifest.js";
15
- function fieldPathToAuditPath(field) {
16
- if (field.startsWith("/")) {
17
- return field;
18
- }
19
- return `/payload/${field.replace(/\./g, "/")}`;
20
- }
21
- function mapSensitivity(level) {
22
- switch (level) {
23
- case "public":
24
- return "public";
25
- case "internal":
26
- return "internal";
27
- case "confidential":
28
- return "private";
29
- case "restricted":
30
- return "sensitive";
31
- default:
32
- return "internal";
33
- }
34
- }
35
- /**
36
- * Apply field redaction rules and lift manifests to audit path vocabulary (observability-system.detail §2).
37
- */
38
- export function redactAuditEvent(payload) {
39
- const { redacted, manifest } = redactEvent(payload);
40
- const redaction = {
41
- manifestId: manifest.id,
42
- maskedPaths: manifest.maskedFields.map(fieldPathToAuditPath),
43
- erasedPaths: manifest.erasedFields.map(fieldPathToAuditPath),
44
- hashedPaths: manifest.hashedFields.map(fieldPathToAuditPath),
45
- contentRefPaths: [],
46
- sensitivity: mapSensitivity(manifest.sensitivityLevel),
47
- };
48
- return { payload: redacted, redaction };
49
- }
50
- function stableStringify(value) {
51
- if (value === null || typeof value !== "object") {
52
- return JSON.stringify(value);
53
- }
54
- if (Array.isArray(value)) {
55
- return `[${value.map((v) => stableStringify(v)).join(",")}]`;
56
- }
57
- const obj = value;
58
- const keys = Object.keys(obj).sort();
59
- return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
60
- }
61
- function hashRecord(input) {
62
- const canonical = stableStringify({
63
- eventId: input.eventId,
64
- family: input.family,
65
- plane: input.plane,
66
- traceId: input.traceId,
67
- sequence: input.sequence,
68
- createdAt: input.createdAt,
69
- payload: input.payload,
70
- redaction: input.redaction,
71
- previousHash: input.previousHash ?? null,
72
- });
73
- return crypto.createHash("sha256").update(canonical, "utf8").digest("hex");
74
- }
75
- /** Recompute integrity hash for an existing envelope (T5.2.2 / verifyAuditHashChain). */
76
- export function computeAuditRecordHash(envelope) {
77
- return hashRecord({
78
- eventId: envelope.eventId,
79
- family: envelope.family,
80
- plane: envelope.plane,
81
- traceId: envelope.traceId,
82
- sequence: envelope.sequence,
83
- createdAt: envelope.createdAt,
84
- payload: envelope.payload,
85
- redaction: envelope.redaction,
86
- previousHash: envelope.integrity.previousHash,
87
- });
88
- }
89
- export function buildAuditEnvelope(input) {
90
- const { payload, redaction } = redactAuditEvent(input.payload);
91
- const eventId = input.eventId ?? crypto.randomUUID();
92
- const createdAt = input.createdAt ?? new Date().toISOString();
93
- const recordHash = hashRecord({
94
- eventId,
95
- family: input.family,
96
- plane: input.plane,
97
- traceId: input.traceId,
98
- sequence: input.sequence,
99
- createdAt,
100
- payload,
101
- redaction,
102
- previousHash: input.previousHash,
103
- });
104
- return {
105
- eventId,
106
- family: input.family,
107
- plane: input.plane,
108
- traceId: input.traceId,
109
- sequence: input.sequence,
110
- createdAt,
111
- payload,
112
- redaction,
113
- integrity: {
114
- previousHash: input.previousHash,
115
- recordHash,
116
- schemaVersion: "observability.v5",
117
- },
118
- };
119
- }
120
- /** @internal Maps legacy field manifest to audit manifest for persistence helpers. */
121
- export function auditManifestFromFieldManifest(manifest) {
122
- return {
123
- manifestId: manifest.id,
124
- maskedPaths: manifest.maskedFields.map(fieldPathToAuditPath),
125
- erasedPaths: manifest.erasedFields.map(fieldPathToAuditPath),
126
- hashedPaths: manifest.hashedFields.map(fieldPathToAuditPath),
127
- contentRefPaths: [],
128
- sensitivity: mapSensitivity(manifest.sensitivityLevel),
129
- };
130
- }
1
+ /**
2
+ * Audit envelope construction + redaction for observability-system v5.
3
+ *
4
+ * Core logic: redact structured payloads, attach RedactionManifest (L0/L1 contract paths),
5
+ * compute hash-chain fields for append-only ledger rows.
6
+ *
7
+ * Dependencies: observability redactEvent (field-level); maps to envelope redaction paths.
8
+ *
9
+ * Boundaries: persistence is delegated to AppendOnlyAuditStore / DB adapters.
10
+ *
11
+ * Test coverage: tests/unit/observability/audit-envelope.test.ts
12
+ */
13
+ import * as crypto from "node:crypto";
14
+ import { redactEvent } from "../redaction/manifest.js";
15
+ function fieldPathToAuditPath(field) {
16
+ if (field.startsWith("/")) {
17
+ return field;
18
+ }
19
+ return `/payload/${field.replace(/\./g, "/")}`;
20
+ }
21
+ function mapSensitivity(level) {
22
+ switch (level) {
23
+ case "public":
24
+ return "public";
25
+ case "internal":
26
+ return "internal";
27
+ case "confidential":
28
+ return "private";
29
+ case "restricted":
30
+ return "sensitive";
31
+ default:
32
+ return "internal";
33
+ }
34
+ }
35
+ /**
36
+ * Apply field redaction rules and lift manifests to audit path vocabulary (observability-system.detail §2).
37
+ */
38
+ export function redactAuditEvent(payload) {
39
+ const { redacted, manifest } = redactEvent(payload);
40
+ const redaction = {
41
+ manifestId: manifest.id,
42
+ maskedPaths: manifest.maskedFields.map(fieldPathToAuditPath),
43
+ erasedPaths: manifest.erasedFields.map(fieldPathToAuditPath),
44
+ hashedPaths: manifest.hashedFields.map(fieldPathToAuditPath),
45
+ contentRefPaths: [],
46
+ sensitivity: mapSensitivity(manifest.sensitivityLevel),
47
+ };
48
+ return { payload: redacted, redaction };
49
+ }
50
+ function stableStringify(value) {
51
+ if (value === null || typeof value !== "object") {
52
+ return JSON.stringify(value);
53
+ }
54
+ if (Array.isArray(value)) {
55
+ return `[${value.map((v) => stableStringify(v)).join(",")}]`;
56
+ }
57
+ const obj = value;
58
+ const keys = Object.keys(obj).sort();
59
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
60
+ }
61
+ function hashRecord(input) {
62
+ const canonical = stableStringify({
63
+ eventId: input.eventId,
64
+ family: input.family,
65
+ plane: input.plane,
66
+ traceId: input.traceId,
67
+ sequence: input.sequence,
68
+ createdAt: input.createdAt,
69
+ payload: input.payload,
70
+ redaction: input.redaction,
71
+ previousHash: input.previousHash ?? null,
72
+ });
73
+ return crypto.createHash("sha256").update(canonical, "utf8").digest("hex");
74
+ }
75
+ /** Recompute integrity hash for an existing envelope (T5.2.2 / verifyAuditHashChain). */
76
+ export function computeAuditRecordHash(envelope) {
77
+ return hashRecord({
78
+ eventId: envelope.eventId,
79
+ family: envelope.family,
80
+ plane: envelope.plane,
81
+ traceId: envelope.traceId,
82
+ sequence: envelope.sequence,
83
+ createdAt: envelope.createdAt,
84
+ payload: envelope.payload,
85
+ redaction: envelope.redaction,
86
+ previousHash: envelope.integrity.previousHash,
87
+ });
88
+ }
89
+ export function buildAuditEnvelope(input) {
90
+ const { payload, redaction } = redactAuditEvent(input.payload);
91
+ const eventId = input.eventId ?? crypto.randomUUID();
92
+ const createdAt = input.createdAt ?? new Date().toISOString();
93
+ const recordHash = hashRecord({
94
+ eventId,
95
+ family: input.family,
96
+ plane: input.plane,
97
+ traceId: input.traceId,
98
+ sequence: input.sequence,
99
+ createdAt,
100
+ payload,
101
+ redaction,
102
+ previousHash: input.previousHash,
103
+ });
104
+ return {
105
+ eventId,
106
+ family: input.family,
107
+ plane: input.plane,
108
+ traceId: input.traceId,
109
+ sequence: input.sequence,
110
+ createdAt,
111
+ payload,
112
+ redaction,
113
+ integrity: {
114
+ previousHash: input.previousHash,
115
+ recordHash,
116
+ schemaVersion: "observability.v5",
117
+ },
118
+ };
119
+ }
120
+ /** @internal Maps legacy field manifest to audit manifest for persistence helpers. */
121
+ export function auditManifestFromFieldManifest(manifest) {
122
+ return {
123
+ manifestId: manifest.id,
124
+ maskedPaths: manifest.maskedFields.map(fieldPathToAuditPath),
125
+ erasedPaths: manifest.erasedFields.map(fieldPathToAuditPath),
126
+ hashedPaths: manifest.hashedFields.map(fieldPathToAuditPath),
127
+ contentRefPaths: [],
128
+ sensitivity: mapSensitivity(manifest.sensitivityLevel),
129
+ };
130
+ }
@@ -1,23 +1,23 @@
1
- import type { AppendOnlyAuditStore } from "./append-only-audit-store.js";
2
- import { type AuditEnvelope, type AuditEventFamily } from "./audit-envelope.js";
3
- export interface AuditExportRange {
4
- from: string;
5
- to: string;
6
- families?: AuditEventFamily[];
7
- }
8
- export type AuditHashChainVerificationStatus = "pass" | "broken" | "incomplete";
9
- export interface AuditHashChainVerificationReport {
10
- reportId: string;
11
- generatedAt: string;
12
- range: AuditExportRange;
13
- checkedEventCount: number;
14
- status: AuditHashChainVerificationStatus;
15
- brokenAtEventIds: string[];
16
- reasons: string[];
17
- }
18
- export interface VerifyAuditHashChainDeps {
19
- loadRange(from: string, to: string, families?: AuditEventFamily[]): Promise<readonly AuditEnvelope<unknown>[]>;
20
- }
21
- export declare function verifyAuditHashChain(range: AuditExportRange, deps: VerifyAuditHashChainDeps): Promise<AuditHashChainVerificationReport>;
22
- /** In-memory adapter: filter `AppendOnlyAuditStore.list()` by createdAt + optional families. */
23
- export declare function createAppendOnlyAuditStoreRangeLoader(store: AppendOnlyAuditStore): VerifyAuditHashChainDeps;
1
+ import type { AppendOnlyAuditStore } from "./append-only-audit-store.js";
2
+ import { type AuditEnvelope, type AuditEventFamily } from "./audit-envelope.js";
3
+ export interface AuditExportRange {
4
+ from: string;
5
+ to: string;
6
+ families?: AuditEventFamily[];
7
+ }
8
+ export type AuditHashChainVerificationStatus = "pass" | "broken" | "incomplete";
9
+ export interface AuditHashChainVerificationReport {
10
+ reportId: string;
11
+ generatedAt: string;
12
+ range: AuditExportRange;
13
+ checkedEventCount: number;
14
+ status: AuditHashChainVerificationStatus;
15
+ brokenAtEventIds: string[];
16
+ reasons: string[];
17
+ }
18
+ export interface VerifyAuditHashChainDeps {
19
+ loadRange(from: string, to: string, families?: AuditEventFamily[]): Promise<readonly AuditEnvelope<unknown>[]>;
20
+ }
21
+ export declare function verifyAuditHashChain(range: AuditExportRange, deps: VerifyAuditHashChainDeps): Promise<AuditHashChainVerificationReport>;
22
+ /** In-memory adapter: filter `AppendOnlyAuditStore.list()` by createdAt + optional families. */
23
+ export declare function createAppendOnlyAuditStoreRangeLoader(store: AppendOnlyAuditStore): VerifyAuditHashChainDeps;
@@ -1,83 +1,83 @@
1
- /**
2
- * Range-based hash-chain verification for append-only audit rows (T5.2.2 / INT-S3).
3
- *
4
- * Core logic: load events in [from, to], order by sequence, recompute recordHash and
5
- * verify previousHash links between consecutive **loaded** rows only (partial ranges
6
- * may start mid-chain; parent outside the slice is not validated). Empty or invalid
7
- * ranges yield incomplete (T5.2.2 / task verification plan).
8
- *
9
- * Dependencies: computeAuditRecordHash from audit-envelope; callers supply loadRange via deps.
10
- *
11
- * Test coverage: tests/unit/observability/verify-audit-hash-chain.test.ts
12
- */
13
- import * as crypto from "node:crypto";
14
- import { computeAuditRecordHash } from "./audit-envelope.js";
15
- function unique(ids) {
16
- return [...new Set(ids)];
17
- }
18
- export async function verifyAuditHashChain(range, deps) {
19
- const generatedAt = new Date().toISOString();
20
- const reportId = crypto.randomUUID();
21
- if (range.from > range.to) {
22
- return {
23
- reportId,
24
- generatedAt,
25
- range,
26
- checkedEventCount: 0,
27
- status: "incomplete",
28
- brokenAtEventIds: [],
29
- reasons: ["invalid_range_from_after_to"],
30
- };
31
- }
32
- const raw = await deps.loadRange(range.from, range.to, range.families);
33
- const events = [...raw].sort((a, b) => a.sequence - b.sequence);
34
- if (events.length === 0) {
35
- return {
36
- reportId,
37
- generatedAt,
38
- range,
39
- checkedEventCount: 0,
40
- status: "incomplete",
41
- brokenAtEventIds: [],
42
- reasons: ["range_empty"],
43
- };
44
- }
45
- const brokenAtEventIds = [];
46
- for (let i = 0; i < events.length; i += 1) {
47
- const event = events[i];
48
- const expected = computeAuditRecordHash(event);
49
- if (event.integrity.recordHash !== expected) {
50
- brokenAtEventIds.push(event.eventId);
51
- }
52
- const prev = events[i - 1];
53
- if (prev && event.integrity.previousHash !== prev.integrity.recordHash) {
54
- brokenAtEventIds.push(event.eventId);
55
- }
56
- }
57
- const uniq = unique(brokenAtEventIds);
58
- const broken = uniq.length > 0;
59
- return {
60
- reportId,
61
- generatedAt,
62
- range,
63
- checkedEventCount: events.length,
64
- status: broken ? "broken" : "pass",
65
- brokenAtEventIds: uniq,
66
- reasons: broken ? ["hash_chain_broken"] : ["hash_chain_valid"],
67
- };
68
- }
69
- /** In-memory adapter: filter `AppendOnlyAuditStore.list()` by createdAt + optional families. */
70
- export function createAppendOnlyAuditStoreRangeLoader(store) {
71
- return {
72
- async loadRange(from, to, families) {
73
- const fams = families?.length ? new Set(families) : undefined;
74
- return store.list().filter((e) => {
75
- if (e.createdAt < from || e.createdAt > to)
76
- return false;
77
- if (fams && !fams.has(e.family))
78
- return false;
79
- return true;
80
- });
81
- },
82
- };
83
- }
1
+ /**
2
+ * Range-based hash-chain verification for append-only audit rows (T5.2.2 / INT-S3).
3
+ *
4
+ * Core logic: load events in [from, to], order by sequence, recompute recordHash and
5
+ * verify previousHash links between consecutive **loaded** rows only (partial ranges
6
+ * may start mid-chain; parent outside the slice is not validated). Empty or invalid
7
+ * ranges yield incomplete (T5.2.2 / task verification plan).
8
+ *
9
+ * Dependencies: computeAuditRecordHash from audit-envelope; callers supply loadRange via deps.
10
+ *
11
+ * Test coverage: tests/unit/observability/verify-audit-hash-chain.test.ts
12
+ */
13
+ import * as crypto from "node:crypto";
14
+ import { computeAuditRecordHash } from "./audit-envelope.js";
15
+ function unique(ids) {
16
+ return [...new Set(ids)];
17
+ }
18
+ export async function verifyAuditHashChain(range, deps) {
19
+ const generatedAt = new Date().toISOString();
20
+ const reportId = crypto.randomUUID();
21
+ if (range.from > range.to) {
22
+ return {
23
+ reportId,
24
+ generatedAt,
25
+ range,
26
+ checkedEventCount: 0,
27
+ status: "incomplete",
28
+ brokenAtEventIds: [],
29
+ reasons: ["invalid_range_from_after_to"],
30
+ };
31
+ }
32
+ const raw = await deps.loadRange(range.from, range.to, range.families);
33
+ const events = [...raw].sort((a, b) => a.sequence - b.sequence);
34
+ if (events.length === 0) {
35
+ return {
36
+ reportId,
37
+ generatedAt,
38
+ range,
39
+ checkedEventCount: 0,
40
+ status: "incomplete",
41
+ brokenAtEventIds: [],
42
+ reasons: ["range_empty"],
43
+ };
44
+ }
45
+ const brokenAtEventIds = [];
46
+ for (let i = 0; i < events.length; i += 1) {
47
+ const event = events[i];
48
+ const expected = computeAuditRecordHash(event);
49
+ if (event.integrity.recordHash !== expected) {
50
+ brokenAtEventIds.push(event.eventId);
51
+ }
52
+ const prev = events[i - 1];
53
+ if (prev && event.integrity.previousHash !== prev.integrity.recordHash) {
54
+ brokenAtEventIds.push(event.eventId);
55
+ }
56
+ }
57
+ const uniq = unique(brokenAtEventIds);
58
+ const broken = uniq.length > 0;
59
+ return {
60
+ reportId,
61
+ generatedAt,
62
+ range,
63
+ checkedEventCount: events.length,
64
+ status: broken ? "broken" : "pass",
65
+ brokenAtEventIds: uniq,
66
+ reasons: broken ? ["hash_chain_broken"] : ["hash_chain_valid"],
67
+ };
68
+ }
69
+ /** In-memory adapter: filter `AppendOnlyAuditStore.list()` by createdAt + optional families. */
70
+ export function createAppendOnlyAuditStoreRangeLoader(store) {
71
+ return {
72
+ async loadRange(from, to, families) {
73
+ const fams = families?.length ? new Set(families) : undefined;
74
+ return store.list().filter((e) => {
75
+ if (e.createdAt < from || e.createdAt > to)
76
+ return false;
77
+ if (fams && !fams.has(e.family))
78
+ return false;
79
+ return true;
80
+ });
81
+ },
82
+ };
83
+ }