@fluxpointstudios/orynq-sdk-flight-recorder 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/dist/crypto/compression.d.ts +37 -0
  3. package/dist/crypto/compression.d.ts.map +1 -0
  4. package/dist/crypto/compression.js +84 -0
  5. package/dist/crypto/compression.js.map +1 -0
  6. package/dist/crypto/encryption.d.ts +44 -0
  7. package/dist/crypto/encryption.d.ts.map +1 -0
  8. package/dist/crypto/encryption.js +129 -0
  9. package/dist/crypto/encryption.js.map +1 -0
  10. package/dist/crypto/hashing.d.ts +63 -0
  11. package/dist/crypto/hashing.d.ts.map +1 -0
  12. package/dist/crypto/hashing.js +182 -0
  13. package/dist/crypto/hashing.js.map +1 -0
  14. package/dist/crypto/index.d.ts +4 -0
  15. package/dist/crypto/index.d.ts.map +1 -0
  16. package/dist/crypto/index.js +4 -0
  17. package/dist/crypto/index.js.map +1 -0
  18. package/dist/index.d.ts +48 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +54 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/integration/index.d.ts +2 -0
  23. package/dist/integration/index.d.ts.map +1 -0
  24. package/dist/integration/index.js +2 -0
  25. package/dist/integration/index.js.map +1 -0
  26. package/dist/integration/openclaw-adapter.d.ts +64 -0
  27. package/dist/integration/openclaw-adapter.d.ts.map +1 -0
  28. package/dist/integration/openclaw-adapter.js +137 -0
  29. package/dist/integration/openclaw-adapter.js.map +1 -0
  30. package/dist/manifest/index.d.ts +2 -0
  31. package/dist/manifest/index.d.ts.map +1 -0
  32. package/dist/manifest/index.js +2 -0
  33. package/dist/manifest/index.js.map +1 -0
  34. package/dist/manifest/manifest-builder.d.ts +72 -0
  35. package/dist/manifest/manifest-builder.d.ts.map +1 -0
  36. package/dist/manifest/manifest-builder.js +84 -0
  37. package/dist/manifest/manifest-builder.js.map +1 -0
  38. package/dist/recorder/chunk-manager.d.ts +56 -0
  39. package/dist/recorder/chunk-manager.d.ts.map +1 -0
  40. package/dist/recorder/chunk-manager.js +172 -0
  41. package/dist/recorder/chunk-manager.js.map +1 -0
  42. package/dist/recorder/event-buffer.d.ts +61 -0
  43. package/dist/recorder/event-buffer.d.ts.map +1 -0
  44. package/dist/recorder/event-buffer.js +101 -0
  45. package/dist/recorder/event-buffer.js.map +1 -0
  46. package/dist/recorder/index.d.ts +4 -0
  47. package/dist/recorder/index.d.ts.map +1 -0
  48. package/dist/recorder/index.js +4 -0
  49. package/dist/recorder/index.js.map +1 -0
  50. package/dist/recorder/stream-recorder.d.ts +66 -0
  51. package/dist/recorder/stream-recorder.d.ts.map +1 -0
  52. package/dist/recorder/stream-recorder.js +329 -0
  53. package/dist/recorder/stream-recorder.js.map +1 -0
  54. package/dist/storage/index.d.ts +2 -0
  55. package/dist/storage/index.d.ts.map +1 -0
  56. package/dist/storage/index.js +2 -0
  57. package/dist/storage/index.js.map +1 -0
  58. package/dist/storage/local-adapter.d.ts +65 -0
  59. package/dist/storage/local-adapter.d.ts.map +1 -0
  60. package/dist/storage/local-adapter.js +152 -0
  61. package/dist/storage/local-adapter.js.map +1 -0
  62. package/dist/types.d.ts +284 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +28 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +43 -0
  67. package/src/__tests__/flight-recorder.test.ts +211 -0
  68. package/src/crypto/compression.ts +105 -0
  69. package/src/crypto/encryption.ts +210 -0
  70. package/src/crypto/hashing.ts +225 -0
  71. package/src/crypto/index.ts +3 -0
  72. package/src/index.ts +96 -0
  73. package/src/integration/index.ts +1 -0
  74. package/src/integration/openclaw-adapter.ts +206 -0
  75. package/src/manifest/index.ts +1 -0
  76. package/src/manifest/manifest-builder.ts +152 -0
  77. package/src/recorder/chunk-manager.ts +224 -0
  78. package/src/recorder/event-buffer.ts +124 -0
  79. package/src/recorder/index.ts +3 -0
  80. package/src/recorder/stream-recorder.ts +427 -0
  81. package/src/storage/index.ts +1 -0
  82. package/src/storage/local-adapter.ts +181 -0
  83. package/src/types.ts +347 -0
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Core types for the PoI Flight Recorder.
3
+ */
4
+ export type TeeType = "sev-snp" | "tdx" | "sgx" | "nitro" | "gpu-cc";
5
+ export type Visibility = "public" | "private" | "redacted";
6
+ export interface EncryptionConfig {
7
+ algorithm: "aes-256-gcm" | "chacha20-poly1305";
8
+ keyDerivation: "hkdf-sha256";
9
+ keyMode: {
10
+ type: "ephemeral";
11
+ } | {
12
+ type: "sealed";
13
+ teeType: TeeType;
14
+ } | {
15
+ type: "wrapped";
16
+ pubkeys: string[];
17
+ };
18
+ }
19
+ export interface RecorderConfig {
20
+ agentId: string;
21
+ sessionId?: string;
22
+ chunkSizeBytes: number;
23
+ chunkTimeoutMs?: number;
24
+ encryption: EncryptionConfig;
25
+ storage: StorageAdapter;
26
+ attestor?: Attestor;
27
+ captureRuntime?: boolean;
28
+ }
29
+ export interface StorageAdapter {
30
+ readonly type: "local" | "ipfs" | "s3" | "arweave";
31
+ store(data: Uint8Array): Promise<StorageRef>;
32
+ storeManifest(manifest: ManifestV2): Promise<StorageRef>;
33
+ fetch(ref: StorageRef): Promise<Uint8Array>;
34
+ fetchManifest(ref: StorageRef): Promise<ManifestV2>;
35
+ verify(ref: StorageRef): Promise<boolean>;
36
+ delete?(ref: StorageRef): Promise<void>;
37
+ pin?(ref: StorageRef): Promise<void>;
38
+ }
39
+ export interface StorageRef {
40
+ type: "local" | "ipfs" | "s3" | "arweave";
41
+ uri: string;
42
+ hash: string;
43
+ size: number;
44
+ }
45
+ export interface Attestor {
46
+ readonly teeType: TeeType;
47
+ attest(hashToSign: string): Promise<AttestationBundle>;
48
+ getMeasurements(): Promise<Measurements>;
49
+ isAttested(): boolean;
50
+ }
51
+ export interface AttestationBundle {
52
+ teeType: TeeType;
53
+ teeVersion: string;
54
+ evidence: {
55
+ format: "raw" | "base64" | "cbor";
56
+ data?: string;
57
+ hash?: string;
58
+ storageUri?: string;
59
+ };
60
+ binding: {
61
+ hash: string;
62
+ hashType: "rootHash" | "manifestHash" | "merkleRoot";
63
+ timestamp: string;
64
+ };
65
+ verifierPolicy: VerifierPolicy;
66
+ attestorId: string;
67
+ attestorPubkey?: string;
68
+ }
69
+ export interface VerifierPolicy {
70
+ expectedMeasurements?: string[];
71
+ allowedSignerKeys?: string[];
72
+ minFirmwareVersion?: string;
73
+ minSvn?: number;
74
+ checkRevocation?: boolean;
75
+ revocationListUri?: string;
76
+ }
77
+ export interface Measurements {
78
+ firmwareVersion?: string;
79
+ sevSnp?: {
80
+ launchMeasurement: string;
81
+ guestPolicy: string;
82
+ };
83
+ tdx?: {
84
+ mrTd: string;
85
+ mrConfigId: string;
86
+ tdAttributes: string;
87
+ };
88
+ sgx?: {
89
+ mrEnclave: string;
90
+ mrSigner: string;
91
+ isvProdId: number;
92
+ isvSvn: number;
93
+ };
94
+ nitro?: {
95
+ pcrs: Record<number, string>;
96
+ moduleId: string;
97
+ };
98
+ }
99
+ export type RecorderEvent = InferenceStartEvent | InferenceEndEvent | ToolCallEvent | ToolResultEvent | StreamChunkEvent | DecisionEvent | ErrorEvent | CustomEvent;
100
+ export interface BaseEvent {
101
+ seq: number;
102
+ ts: string;
103
+ spanId?: string;
104
+ }
105
+ export interface InferenceStartEvent extends BaseEvent {
106
+ kind: "inference:start";
107
+ requestId: string;
108
+ model: string;
109
+ promptHash: string;
110
+ systemPromptHash?: string;
111
+ params: InferenceParams;
112
+ }
113
+ export interface InferenceEndEvent extends BaseEvent {
114
+ kind: "inference:end";
115
+ requestId: string;
116
+ outputHash: string;
117
+ tokenCounts: {
118
+ prompt: number;
119
+ completion: number;
120
+ };
121
+ durationMs: number;
122
+ }
123
+ export interface ToolCallEvent extends BaseEvent {
124
+ kind: "tool:call";
125
+ toolName: string;
126
+ argsHash: string;
127
+ visibility: Visibility;
128
+ }
129
+ export interface ToolResultEvent extends BaseEvent {
130
+ kind: "tool:result";
131
+ toolName: string;
132
+ resultHash: string;
133
+ success: boolean;
134
+ visibility: Visibility;
135
+ }
136
+ export interface StreamChunkEvent extends BaseEvent {
137
+ kind: "stream:chunk";
138
+ chunkHash: string;
139
+ index: number;
140
+ }
141
+ export interface DecisionEvent extends BaseEvent {
142
+ kind: "decision";
143
+ decisionType: string;
144
+ choiceHash: string;
145
+ rationale?: string;
146
+ }
147
+ export interface ErrorEvent extends BaseEvent {
148
+ kind: "error";
149
+ errorType: string;
150
+ message: string;
151
+ recoverable: boolean;
152
+ }
153
+ export interface CustomEvent extends BaseEvent {
154
+ kind: "custom";
155
+ eventType: string;
156
+ dataHash: string;
157
+ metadata?: Record<string, unknown>;
158
+ }
159
+ export interface InferenceParams {
160
+ temperature?: number;
161
+ topP?: number;
162
+ topK?: number;
163
+ maxTokens?: number;
164
+ stopStrings?: string[];
165
+ frequencyPenalty?: number;
166
+ presencePenalty?: number;
167
+ }
168
+ export interface ChunkRef {
169
+ id: string;
170
+ hash: string;
171
+ size: number;
172
+ storageUri: string;
173
+ encryptionKeyId: string;
174
+ compression: "zstd" | "gzip" | "none";
175
+ }
176
+ export interface ChunkData {
177
+ ciphertext: Uint8Array;
178
+ nonce: Uint8Array;
179
+ tag: Uint8Array;
180
+ meta: {
181
+ index: number;
182
+ eventRange: [number, number];
183
+ spanIds: string[];
184
+ createdAt: string;
185
+ };
186
+ }
187
+ export interface ManifestV2 {
188
+ formatVersion: "2.0";
189
+ agentId: string;
190
+ sessionId: string;
191
+ rootHash: string;
192
+ merkleRoot: string;
193
+ manifestHash: string;
194
+ inputs: {
195
+ promptHash: string;
196
+ systemPromptHash?: string;
197
+ toolContextHash?: string;
198
+ encryptedCiphertextHashes?: string[];
199
+ };
200
+ params: {
201
+ model: string;
202
+ modelWeightDigest?: string;
203
+ tokenizerDigest?: string;
204
+ temperature?: number;
205
+ topP?: number;
206
+ topK?: number;
207
+ maxTokens?: number;
208
+ stopStrings?: string[];
209
+ frequencyPenalty?: number;
210
+ presencePenalty?: number;
211
+ toolPolicy?: Record<string, unknown>;
212
+ };
213
+ runtime: {
214
+ recorderVersion: string;
215
+ containerDigest?: string;
216
+ gitCommit?: string;
217
+ dependencies?: Record<string, string>;
218
+ gpuDriver?: string;
219
+ nodeVersion?: string;
220
+ };
221
+ chunks: ChunkRef[];
222
+ outputs: {
223
+ transcriptRollingHash: string;
224
+ toolCallCount: number;
225
+ totalTokens: number;
226
+ completionTokens: number;
227
+ };
228
+ attestation?: {
229
+ teeType: TeeType;
230
+ evidenceHash: string;
231
+ evidenceUri?: string;
232
+ verifierPolicy: VerifierPolicy;
233
+ boundHash: "rootHash" | "manifestHash" | "merkleRoot";
234
+ };
235
+ createdAt: string;
236
+ startedAt: string;
237
+ endedAt: string;
238
+ durationMs: number;
239
+ totalEvents: number;
240
+ totalSpans: number;
241
+ }
242
+ export interface RecordingSession {
243
+ sessionId: string;
244
+ agentId: string;
245
+ startedAt: string;
246
+ status: "recording" | "finalizing" | "finalized" | "aborted";
247
+ }
248
+ export interface RecordingResult {
249
+ rootHash: string;
250
+ merkleRoot: string;
251
+ manifestHash: string;
252
+ manifest: ManifestV2;
253
+ chunkRefs: ChunkRef[];
254
+ attestation?: AttestationBundle;
255
+ anchorEntry: AnchorEntry;
256
+ }
257
+ export interface AnchorEntry {
258
+ schema: "poi-anchor-v2";
259
+ rootHash: string;
260
+ merkleRoot: string;
261
+ manifestHash: string;
262
+ storageUri: string;
263
+ agentId: string;
264
+ sessionId: string;
265
+ timestamp: string;
266
+ }
267
+ export declare enum FlightRecorderError {
268
+ RECORDING_NOT_STARTED = 1001,
269
+ RECORDING_ALREADY_FINALIZED = 1002,
270
+ CHUNK_CREATION_FAILED = 1003,
271
+ ENCRYPTION_FAILED = 1004,
272
+ COMPRESSION_FAILED = 1005,
273
+ STORAGE_WRITE_FAILED = 2001,
274
+ STORAGE_READ_FAILED = 2002,
275
+ STORAGE_NOT_FOUND = 2003,
276
+ ATTESTATION_NOT_AVAILABLE = 3001,
277
+ ATTESTATION_FAILED = 3002
278
+ }
279
+ export declare class FlightRecorderException extends Error {
280
+ readonly code: FlightRecorderError;
281
+ readonly cause?: Error | undefined;
282
+ constructor(code: FlightRecorderError, message: string, cause?: Error | undefined);
283
+ }
284
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Core types for the PoI Flight Recorder.
3
+ */
4
+ // === Errors ===
5
+ export var FlightRecorderError;
6
+ (function (FlightRecorderError) {
7
+ FlightRecorderError[FlightRecorderError["RECORDING_NOT_STARTED"] = 1001] = "RECORDING_NOT_STARTED";
8
+ FlightRecorderError[FlightRecorderError["RECORDING_ALREADY_FINALIZED"] = 1002] = "RECORDING_ALREADY_FINALIZED";
9
+ FlightRecorderError[FlightRecorderError["CHUNK_CREATION_FAILED"] = 1003] = "CHUNK_CREATION_FAILED";
10
+ FlightRecorderError[FlightRecorderError["ENCRYPTION_FAILED"] = 1004] = "ENCRYPTION_FAILED";
11
+ FlightRecorderError[FlightRecorderError["COMPRESSION_FAILED"] = 1005] = "COMPRESSION_FAILED";
12
+ FlightRecorderError[FlightRecorderError["STORAGE_WRITE_FAILED"] = 2001] = "STORAGE_WRITE_FAILED";
13
+ FlightRecorderError[FlightRecorderError["STORAGE_READ_FAILED"] = 2002] = "STORAGE_READ_FAILED";
14
+ FlightRecorderError[FlightRecorderError["STORAGE_NOT_FOUND"] = 2003] = "STORAGE_NOT_FOUND";
15
+ FlightRecorderError[FlightRecorderError["ATTESTATION_NOT_AVAILABLE"] = 3001] = "ATTESTATION_NOT_AVAILABLE";
16
+ FlightRecorderError[FlightRecorderError["ATTESTATION_FAILED"] = 3002] = "ATTESTATION_FAILED";
17
+ })(FlightRecorderError || (FlightRecorderError = {}));
18
+ export class FlightRecorderException extends Error {
19
+ code;
20
+ cause;
21
+ constructor(code, message, cause) {
22
+ super(message);
23
+ this.code = code;
24
+ this.cause = cause;
25
+ this.name = "FlightRecorderException";
26
+ }
27
+ }
28
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgUH,iBAAiB;AAEjB,MAAM,CAAN,IAAY,mBAWX;AAXD,WAAY,mBAAmB;IAC7B,kGAA4B,CAAA;IAC5B,8GAAkC,CAAA;IAClC,kGAA4B,CAAA;IAC5B,0FAAwB,CAAA;IACxB,4FAAyB,CAAA;IACzB,gGAA2B,CAAA;IAC3B,8FAA0B,CAAA;IAC1B,0FAAwB,CAAA;IACxB,0GAAgC,CAAA;IAChC,4FAAyB,CAAA;AAC3B,CAAC,EAXW,mBAAmB,KAAnB,mBAAmB,QAW9B;AAED,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAE9B;IAEA;IAHlB,YACkB,IAAyB,EACzC,OAAe,EACC,KAAa;QAE7B,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAqB;QAEzB,UAAK,GAAL,KAAK,CAAQ;QAG7B,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@fluxpointstudios/orynq-sdk-flight-recorder",
3
+ "version": "0.1.0",
4
+ "description": "Streaming flight recorder for PoI with chunking, compression, and encryption",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "fflate": "^0.8.2",
16
+ "@fluxpointstudios/orynq-sdk-core": "0.1.0",
17
+ "@fluxpointstudios/orynq-sdk-process-trace": "0.1.0"
18
+ },
19
+ "devDependencies": {
20
+ "vitest": "^1.6.0"
21
+ },
22
+ "peerDependencies": {
23
+ "typescript": "^5.0.0"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "src"
28
+ ],
29
+ "keywords": [
30
+ "poi",
31
+ "proof-of-inference",
32
+ "flight-recorder",
33
+ "cardano",
34
+ "audit"
35
+ ],
36
+ "license": "MIT",
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.json",
39
+ "test": "vitest run",
40
+ "test:watch": "vitest",
41
+ "lint": "eslint src --ext .ts"
42
+ }
43
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Tests for flight-recorder package.
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach } from "vitest";
6
+ import { FlightRecorder } from "../recorder/stream-recorder.js";
7
+ import { LocalStorageAdapter } from "../storage/local-adapter.js";
8
+ import { sha256String, buildMerkleRoot, merkleLeaf } from "../crypto/hashing.js";
9
+ import { generateKey, encrypt, decrypt } from "../crypto/encryption.js";
10
+ import { compress, decompress } from "../crypto/compression.js";
11
+ import type { RecorderConfig, StorageAdapter, StorageRef, ManifestV2 } from "../types.js";
12
+ import * as fs from "node:fs/promises";
13
+ import * as path from "node:path";
14
+ import * as os from "node:os";
15
+
16
+ describe("Crypto utilities", () => {
17
+ describe("sha256", () => {
18
+ it("should hash strings consistently", async () => {
19
+ const hash1 = await sha256String("hello");
20
+ const hash2 = await sha256String("hello");
21
+ expect(hash1).toBe(hash2);
22
+ expect(hash1).toHaveLength(64); // 32 bytes hex
23
+ });
24
+
25
+ it("should produce different hashes for different inputs", async () => {
26
+ const hash1 = await sha256String("hello");
27
+ const hash2 = await sha256String("world");
28
+ expect(hash1).not.toBe(hash2);
29
+ });
30
+ });
31
+
32
+ describe("encryption", () => {
33
+ it("should encrypt and decrypt data", async () => {
34
+ const key = await generateKey();
35
+ const plaintext = new TextEncoder().encode("secret message");
36
+
37
+ const encrypted = await encrypt(plaintext, key);
38
+ expect(encrypted.ciphertext).not.toEqual(plaintext);
39
+
40
+ const decrypted = await decrypt(encrypted, key);
41
+ expect(new TextDecoder().decode(decrypted)).toBe("secret message");
42
+ });
43
+
44
+ it("should encrypt with additional data", async () => {
45
+ const key = await generateKey();
46
+ const plaintext = new TextEncoder().encode("secret");
47
+ const aad = new TextEncoder().encode("metadata");
48
+
49
+ const encrypted = await encrypt(plaintext, key, aad);
50
+ const decrypted = await decrypt(encrypted, key, aad);
51
+ expect(new TextDecoder().decode(decrypted)).toBe("secret");
52
+ });
53
+ });
54
+
55
+ describe("compression", () => {
56
+ it("should compress and decompress data", async () => {
57
+ const original = new TextEncoder().encode("hello world ".repeat(100));
58
+ const compressed = await compress(original, "gzip");
59
+ expect(compressed.data.length).toBeLessThan(original.length);
60
+
61
+ const decompressed = await decompress(compressed.data, "gzip");
62
+ expect(new TextDecoder().decode(decompressed)).toBe("hello world ".repeat(100));
63
+ });
64
+ });
65
+
66
+ describe("merkle tree", () => {
67
+ it("should build merkle root from leaf hashes", async () => {
68
+ const leaves = await Promise.all([
69
+ merkleLeaf(new TextEncoder().encode("leaf1")),
70
+ merkleLeaf(new TextEncoder().encode("leaf2")),
71
+ merkleLeaf(new TextEncoder().encode("leaf3")),
72
+ ]);
73
+
74
+ const root = await buildMerkleRoot(leaves);
75
+ expect(root).toHaveLength(64);
76
+ });
77
+
78
+ it("should return empty tree hash for empty leaves", async () => {
79
+ const root = await buildMerkleRoot([]);
80
+ expect(root).toHaveLength(64);
81
+ });
82
+ });
83
+ });
84
+
85
+ describe("LocalStorageAdapter", () => {
86
+ let tmpDir: string;
87
+ let adapter: LocalStorageAdapter;
88
+
89
+ beforeEach(async () => {
90
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "flight-recorder-test-"));
91
+ adapter = new LocalStorageAdapter({ baseDir: tmpDir });
92
+ });
93
+
94
+ it("should store and fetch data", async () => {
95
+ const data = new TextEncoder().encode("test data");
96
+ const ref = await adapter.store(data);
97
+
98
+ expect(ref.type).toBe("local");
99
+ expect(ref.hash).toHaveLength(64);
100
+ expect(ref.size).toBe(data.length);
101
+
102
+ const fetched = await adapter.fetch(ref);
103
+ expect(new TextDecoder().decode(fetched)).toBe("test data");
104
+ });
105
+
106
+ it("should verify stored data", async () => {
107
+ const data = new TextEncoder().encode("test data");
108
+ const ref = await adapter.store(data);
109
+
110
+ const valid = await adapter.verify(ref);
111
+ expect(valid).toBe(true);
112
+ });
113
+ });
114
+
115
+ describe("FlightRecorder", () => {
116
+ let tmpDir: string;
117
+ let storage: LocalStorageAdapter;
118
+ let config: RecorderConfig;
119
+
120
+ beforeEach(async () => {
121
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "flight-recorder-test-"));
122
+ storage = new LocalStorageAdapter({ baseDir: tmpDir });
123
+ config = {
124
+ agentId: "test-agent",
125
+ chunkSizeBytes: 1024,
126
+ encryption: {
127
+ algorithm: "aes-256-gcm",
128
+ keyDerivation: "hkdf-sha256",
129
+ keyMode: { type: "ephemeral" },
130
+ },
131
+ storage,
132
+ };
133
+ });
134
+
135
+ it("should start a recording session", async () => {
136
+ const recorder = new FlightRecorder(config);
137
+ const session = await recorder.start();
138
+
139
+ expect(session.agentId).toBe("test-agent");
140
+ expect(session.status).toBe("recording");
141
+ expect(session.sessionId).toBeDefined();
142
+ });
143
+
144
+ it("should record events", async () => {
145
+ const recorder = new FlightRecorder(config);
146
+ await recorder.start();
147
+
148
+ await recorder.record({
149
+ kind: "inference:start",
150
+ requestId: "req-1",
151
+ model: "test-model",
152
+ promptHash: "abc123",
153
+ params: { temperature: 0.7 },
154
+ });
155
+
156
+ await recorder.record({
157
+ kind: "inference:end",
158
+ requestId: "req-1",
159
+ outputHash: "def456",
160
+ tokenCounts: { prompt: 10, completion: 20 },
161
+ durationMs: 100,
162
+ });
163
+
164
+ const result = await recorder.finalize();
165
+
166
+ expect(result.manifest.formatVersion).toBe("2.0");
167
+ expect(result.manifest.agentId).toBe("test-agent");
168
+ expect(result.manifest.totalEvents).toBe(2);
169
+ expect(result.rootHash).toHaveLength(64);
170
+ });
171
+
172
+ it("should manage spans", async () => {
173
+ const recorder = new FlightRecorder(config);
174
+ await recorder.start();
175
+
176
+ const spanId = recorder.startSpan("test-span");
177
+ expect(spanId).toBeDefined();
178
+
179
+ await recorder.record({
180
+ kind: "inference:start",
181
+ requestId: "req-1",
182
+ model: "test-model",
183
+ promptHash: "abc123",
184
+ params: {},
185
+ spanId,
186
+ });
187
+
188
+ recorder.endSpan(spanId);
189
+
190
+ const result = await recorder.finalize();
191
+ expect(result.manifest.totalSpans).toBe(1);
192
+ });
193
+
194
+ it("should abort recording", async () => {
195
+ const recorder = new FlightRecorder(config);
196
+ await recorder.start();
197
+
198
+ await recorder.record({
199
+ kind: "inference:start",
200
+ requestId: "req-1",
201
+ model: "test-model",
202
+ promptHash: "abc123",
203
+ params: {},
204
+ });
205
+
206
+ await recorder.abort("test abort");
207
+
208
+ const session = recorder.getSession();
209
+ expect(session?.status).toBe("aborted");
210
+ });
211
+ });
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Compression utilities for flight recorder chunks.
3
+ * Uses fflate for zstd-like compression (actually gzip/deflate, but fast).
4
+ *
5
+ * Note: True zstd would require native bindings. fflate provides
6
+ * excellent pure-JS compression that works everywhere.
7
+ */
8
+
9
+ import { gzip, gunzip, strToU8, strFromU8 } from "fflate";
10
+
11
+ export type CompressionType = "gzip" | "none";
12
+
13
+ export interface CompressionResult {
14
+ data: Uint8Array;
15
+ originalSize: number;
16
+ compressedSize: number;
17
+ ratio: number;
18
+ algorithm: CompressionType;
19
+ }
20
+
21
+ /**
22
+ * Compress data using gzip.
23
+ */
24
+ export function compress(data: Uint8Array, algorithm: CompressionType = "gzip"): Promise<CompressionResult> {
25
+ return new Promise((resolve, reject) => {
26
+ if (algorithm === "none") {
27
+ resolve({
28
+ data,
29
+ originalSize: data.length,
30
+ compressedSize: data.length,
31
+ ratio: 1,
32
+ algorithm: "none",
33
+ });
34
+ return;
35
+ }
36
+
37
+ gzip(data, { level: 6 }, (err, compressed) => {
38
+ if (err) {
39
+ reject(err);
40
+ return;
41
+ }
42
+
43
+ resolve({
44
+ data: compressed,
45
+ originalSize: data.length,
46
+ compressedSize: compressed.length,
47
+ ratio: compressed.length / data.length,
48
+ algorithm: "gzip",
49
+ });
50
+ });
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Decompress gzip data.
56
+ */
57
+ export function decompress(data: Uint8Array, algorithm: CompressionType = "gzip"): Promise<Uint8Array> {
58
+ return new Promise((resolve, reject) => {
59
+ if (algorithm === "none") {
60
+ resolve(data);
61
+ return;
62
+ }
63
+
64
+ gunzip(data, (err, decompressed) => {
65
+ if (err) {
66
+ reject(err);
67
+ return;
68
+ }
69
+
70
+ resolve(decompressed);
71
+ });
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Compress a string to bytes.
77
+ */
78
+ export function compressString(str: string, algorithm: CompressionType = "gzip"): Promise<CompressionResult> {
79
+ const data = strToU8(str);
80
+ return compress(data, algorithm);
81
+ }
82
+
83
+ /**
84
+ * Decompress bytes to a string.
85
+ */
86
+ export async function decompressString(data: Uint8Array, algorithm: CompressionType = "gzip"): Promise<string> {
87
+ const decompressed = await decompress(data, algorithm);
88
+ return strFromU8(decompressed);
89
+ }
90
+
91
+ /**
92
+ * Estimate if compression is worthwhile for the given data.
93
+ * Returns true if data is likely compressible (text, JSON, etc).
94
+ */
95
+ export function isCompressible(data: Uint8Array, sampleSize = 1024): boolean {
96
+ // Sample the beginning of the data
97
+ const sample = data.slice(0, Math.min(sampleSize, data.length));
98
+
99
+ // Count unique bytes - highly compressible data has fewer unique values
100
+ const uniqueBytes = new Set(sample);
101
+ const uniqueRatio = uniqueBytes.size / sample.length;
102
+
103
+ // If less than 60% unique bytes, likely compressible
104
+ return uniqueRatio < 0.6;
105
+ }