@peac/capture-core 0.10.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,291 @@
1
+ # @peac/capture-core
2
+
3
+ Runtime-neutral capture pipeline for PEAC interaction evidence.
4
+
5
+ ## Overview
6
+
7
+ `@peac/capture-core` provides a deterministic, tamper-evident capture pipeline for recording
8
+ agent interactions. It is designed to be runtime-agnostic (no Node.js/filesystem dependencies)
9
+ and can run in any JavaScript environment with WebCrypto support.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pnpm add @peac/capture-core
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { createCaptureSession, createHasher } from '@peac/capture-core';
21
+ import { createInMemorySpoolStore, createInMemoryDedupeIndex } from '@peac/capture-core/testkit';
22
+
23
+ // Create a capture session
24
+ const session = createCaptureSession({
25
+ store: createInMemorySpoolStore(),
26
+ dedupe: createInMemoryDedupeIndex(),
27
+ hasher: createHasher(),
28
+ });
29
+
30
+ // Capture an action
31
+ const result = await session.capture({
32
+ id: 'action-001',
33
+ kind: 'tool.call',
34
+ platform: 'my-agent',
35
+ started_at: new Date().toISOString(),
36
+ tool_name: 'web_search',
37
+ input_bytes: new TextEncoder().encode('{"query": "hello"}'),
38
+ output_bytes: new TextEncoder().encode('{"results": []}'),
39
+ });
40
+
41
+ if (result.success) {
42
+ console.log('Captured:', result.entry.entry_digest);
43
+ }
44
+
45
+ await session.close();
46
+ ```
47
+
48
+ ## Determinism Contract
49
+
50
+ This package guarantees deterministic output for identical inputs. The following behaviors
51
+ are normative and MUST NOT change without a wire format version bump.
52
+
53
+ ### Entry Digest Computation
54
+
55
+ The `entry_digest` is computed by:
56
+
57
+ 1. Serializing the entry (minus `entry_digest` field) using JCS (RFC 8785)
58
+ 2. Computing SHA-256 of the canonical JSON bytes
59
+ 3. Encoding as lowercase hex (64 characters)
60
+
61
+ **Fields included in hash:**
62
+
63
+ - `captured_at` (RFC 3339 timestamp)
64
+ - `action` (full action object, minus `input_bytes`/`output_bytes`)
65
+ - `input_digest` (if present)
66
+ - `output_digest` (if present)
67
+ - `prev_entry_digest` (chain linkage)
68
+ - `sequence` (monotonic counter)
69
+
70
+ ### Genesis Digest
71
+
72
+ The first entry in a chain has `prev_entry_digest` set to `GENESIS_DIGEST`, a
73
+ **protocol-defined sentinel value** consisting of 64 zero characters:
74
+
75
+ ```text
76
+ 0000000000000000000000000000000000000000000000000000000000000000
77
+ ```
78
+
79
+ This is NOT the SHA-256 hash of an empty string (which would be `e3b0c44...`).
80
+ It is an arbitrary constant chosen to be obviously distinguishable and to
81
+ simplify chain verification (check for all-zeros rather than compute a hash).
82
+
83
+ ### Timestamp Derivation
84
+
85
+ `captured_at` is derived deterministically from action timestamps:
86
+
87
+ ```typescript
88
+ captured_at = action.completed_at ?? action.started_at;
89
+ ```
90
+
91
+ This ensures the same action stream produces identical chain digests across sessions.
92
+ Wall-clock time is NOT used.
93
+
94
+ **Monotonicity caveat:** `captured_at` values may be non-monotonic (out of order) even
95
+ though the chain is strictly ordered by sequence number. This can happen when actions
96
+ complete in a different order than they started. The chain ordering is by invocation
97
+ order, NOT by timestamp order.
98
+
99
+ ### Payload Hashing
100
+
101
+ Payloads are hashed according to truncation thresholds:
102
+
103
+ | Size | Algorithm | Label |
104
+ | ------ | ----------------- | ------------------ |
105
+ | <= 1MB | Full SHA-256 | `sha-256` |
106
+ | > 1MB | First 1MB SHA-256 | `sha-256:trunc-1m` |
107
+
108
+ The `bytes` field always contains the original payload size (for audit).
109
+
110
+ ### JCS Canonicalization
111
+
112
+ JSON canonicalization follows RFC 8785 with JavaScript-specific `undefined` handling:
113
+
114
+ - Object properties with `undefined` values are **omitted**
115
+ - Array elements that are `undefined` become **`null`**
116
+ - Top-level `undefined` **throws an error**
117
+
118
+ This matches `JSON.stringify` behavior. See `@peac/crypto` documentation for details.
119
+
120
+ ## Concurrency Contract
121
+
122
+ ### Single-Writer Per Session
123
+
124
+ Each `CaptureSession` instance maintains internal state (sequence number, head digest)
125
+ that is NOT thread-safe across multiple sessions. For concurrent agents:
126
+
127
+ - Create one session per agent/workflow
128
+ - Do NOT share sessions across async boundaries without serialization
129
+
130
+ ### Capture Serialization
131
+
132
+ Concurrent `capture()` calls on the same session are automatically serialized:
133
+
134
+ ```typescript
135
+ // These run sequentially (not in parallel) to maintain chain integrity
136
+ const [r1, r2, r3] = await Promise.all([
137
+ session.capture(action1),
138
+ session.capture(action2),
139
+ session.capture(action3),
140
+ ]);
141
+ ```
142
+
143
+ **Ordering:** Captures are ordered by invocation time (when `capture()` was called),
144
+ NOT by action timestamps. If timestamp-ordered chains are required, sort actions
145
+ before capturing.
146
+
147
+ ### Never-Throw Guarantee
148
+
149
+ `capture()` NEVER throws exceptions. All failures are returned as `CaptureResult`:
150
+
151
+ ```typescript
152
+ const result = await session.capture(action);
153
+ if (!result.success) {
154
+ console.error(result.code, result.message);
155
+ }
156
+ ```
157
+
158
+ Error codes:
159
+
160
+ - `E_CAPTURE_DUPLICATE` - Action ID already captured
161
+ - `E_CAPTURE_INVALID_ACTION` - Missing required fields
162
+ - `E_CAPTURE_HASH_FAILED` - Hashing operation failed
163
+ - `E_CAPTURE_STORE_FAILED` - Storage backend failed
164
+ - `E_CAPTURE_SESSION_CLOSED` - Session was closed
165
+ - `E_CAPTURE_INTERNAL` - Unexpected internal error
166
+
167
+ ### Queue Recovery
168
+
169
+ If a capture fails, subsequent captures can still succeed. The queue is designed to
170
+ be resilient:
171
+
172
+ ```typescript
173
+ const r1 = await session.capture(badAction); // Fails
174
+ const r2 = await session.capture(goodAction); // Succeeds (queue not wedged)
175
+ ```
176
+
177
+ ### Session Lifecycle and close()
178
+
179
+ The `close()` method releases session resources. Its behavior is:
180
+
181
+ **Semantics:**
182
+
183
+ - **Immediate:** `close()` does NOT wait for in-flight captures to drain. Any
184
+ capture already in progress may complete or fail.
185
+ - **Idempotent:** Multiple `close()` calls are safe and have no additional effect.
186
+ - **Terminal:** After `close()`, all subsequent `capture()` calls return
187
+ `E_CAPTURE_SESSION_CLOSED` (never throw).
188
+
189
+ **Best practice:** Wait for all captures to complete before closing:
190
+
191
+ ```typescript
192
+ // Good: wait for captures, then close
193
+ const results = await Promise.all([session.capture(action1), session.capture(action2)]);
194
+ await session.close();
195
+
196
+ // Risky: closing while captures in-flight
197
+ session.capture(action1); // May or may not complete
198
+ await session.close(); // Immediate - doesn't wait
199
+ ```
200
+
201
+ **Resource cleanup:** `close()` calls `store.close()` on the underlying SpoolStore.
202
+ Custom SpoolStore implementations should release file handles, database connections,
203
+ or other resources in their `close()` method.
204
+
205
+ ## API Reference
206
+
207
+ ### Main Exports
208
+
209
+ ```typescript
210
+ import {
211
+ // Constants
212
+ GENESIS_DIGEST, // Protocol-defined sentinel: 64 zeros (NOT sha256 of empty)
213
+ SIZE_CONSTANTS, // { TRUNC_64K: 65536, TRUNC_1M: 1048576 }
214
+
215
+ // Factories
216
+ createHasher, // Create a Hasher instance
217
+ createCaptureSession, // Create a CaptureSession
218
+
219
+ // Mappers
220
+ toInteractionEvidence, // SpoolEntry -> InteractionEvidenceV01
221
+ toInteractionEvidenceBatch, // SpoolEntry[] -> InteractionEvidenceV01[]
222
+
223
+ // Types
224
+ type CapturedAction,
225
+ type SpoolEntry,
226
+ type CaptureResult,
227
+ type Hasher,
228
+ type SpoolStore,
229
+ type DedupeIndex,
230
+ } from '@peac/capture-core';
231
+ ```
232
+
233
+ ### Testkit Exports
234
+
235
+ For testing only. Do NOT use in production:
236
+
237
+ ```typescript
238
+ import {
239
+ createInMemorySpoolStore,
240
+ createInMemoryDedupeIndex,
241
+ InMemorySpoolStore,
242
+ InMemoryDedupeIndex,
243
+ } from '@peac/capture-core/testkit';
244
+ ```
245
+
246
+ ## Implementing Custom Backends
247
+
248
+ ### SpoolStore
249
+
250
+ ```typescript
251
+ interface SpoolStore {
252
+ append(entry: SpoolEntry): Promise<void>;
253
+ getHeadDigest(): Promise<string>;
254
+ getSequence(): Promise<number>;
255
+ commit(): Promise<void>;
256
+ close(): Promise<void>;
257
+ }
258
+ ```
259
+
260
+ ### DedupeIndex
261
+
262
+ ```typescript
263
+ interface DedupeIndex {
264
+ has(actionId: string): Promise<boolean>;
265
+ get(actionId: string): Promise<DedupeEntry | undefined>;
266
+ set(actionId: string, entry: DedupeEntry): Promise<void>;
267
+ markEmitted(actionId: string): Promise<boolean>;
268
+ delete(actionId: string): Promise<boolean>;
269
+ size(): Promise<number>;
270
+ clear(): Promise<void>;
271
+ }
272
+ ```
273
+
274
+ ## Module Format
275
+
276
+ This package ships **CommonJS** output. ESM `import` is supported via Node's CJS interop:
277
+
278
+ ```typescript
279
+ // Both work
280
+ import { createCaptureSession } from '@peac/capture-core'; // ESM (Node synthesizes default)
281
+ const { createCaptureSession } = require('@peac/capture-core'); // CJS
282
+ ```
283
+
284
+ ## Runtime Requirements
285
+
286
+ - **WebCrypto API**: `crypto.subtle` must be available
287
+ - Supported environments: Node.js 18+, Deno, Bun, modern browsers, Cloudflare Workers
288
+
289
+ ## License
290
+
291
+ Apache-2.0
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @peac/capture-core - Action Hasher
3
+ *
4
+ * Deterministic hashing for capture pipeline.
5
+ * Uses @peac/crypto for JCS (RFC 8785) and SHA-256.
6
+ *
7
+ * RUNTIME REQUIREMENT: WebCrypto (crypto.subtle) must be available.
8
+ * Works in: Node.js 18+, Deno, Bun, modern browsers, Cloudflare Workers.
9
+ */
10
+ import type { Digest } from '@peac/schema';
11
+ import type { Hasher, HasherConfig, SpoolEntry } from './types';
12
+ /**
13
+ * Default hasher implementation using JCS + SHA-256.
14
+ *
15
+ * Determinism guarantees:
16
+ * - Same input bytes -> same digest
17
+ * - Same SpoolEntry (minus entry_digest) -> same chain hash
18
+ * - Truncation algorithm is deterministic (first N bytes)
19
+ *
20
+ * Supported truncation thresholds:
21
+ * - 64k (65536 bytes) -> alg: 'sha-256:trunc-64k'
22
+ * - 1m (1048576 bytes) -> alg: 'sha-256:trunc-1m'
23
+ */
24
+ export declare class ActionHasher implements Hasher {
25
+ private readonly truncateThreshold;
26
+ constructor(config?: HasherConfig);
27
+ /**
28
+ * Compute digest for payload bytes.
29
+ * Automatically truncates if payload exceeds threshold.
30
+ */
31
+ digest(payload: Uint8Array): Promise<Digest>;
32
+ /**
33
+ * Compute digest for a spool entry (for chaining).
34
+ * Uses JCS (RFC 8785) for deterministic serialization.
35
+ */
36
+ digestEntry(entry: Omit<SpoolEntry, 'entry_digest'>): Promise<string>;
37
+ }
38
+ /**
39
+ * Create a default hasher instance.
40
+ */
41
+ export declare function createHasher(config?: HasherConfig): Hasher;
42
+ //# sourceMappingURL=hasher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hasher.d.ts","sourceRoot":"","sources":["../src/hasher.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAa,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAwDhE;;;;;;;;;;;GAWG;AACH,qBAAa,YAAa,YAAW,MAAM;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;gBAE/C,MAAM,GAAE,YAAiB;IAiBrC;;;OAGG;IACG,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IA+BlD;;;OAGG;IACG,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;CAM5E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAE1D"}
package/dist/hasher.js ADDED
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ /**
3
+ * @peac/capture-core - Action Hasher
4
+ *
5
+ * Deterministic hashing for capture pipeline.
6
+ * Uses @peac/crypto for JCS (RFC 8785) and SHA-256.
7
+ *
8
+ * RUNTIME REQUIREMENT: WebCrypto (crypto.subtle) must be available.
9
+ * Works in: Node.js 18+, Deno, Bun, modern browsers, Cloudflare Workers.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ActionHasher = void 0;
13
+ exports.createHasher = createHasher;
14
+ const crypto_1 = require("@peac/crypto");
15
+ const types_1 = require("./types");
16
+ // =============================================================================
17
+ // Constants
18
+ // =============================================================================
19
+ /**
20
+ * Valid truncation thresholds (only these are supported).
21
+ * Using other values would produce digests that don't match any declared algorithm.
22
+ */
23
+ const VALID_TRUNCATE_THRESHOLDS = [types_1.SIZE_CONSTANTS.TRUNC_64K, types_1.SIZE_CONSTANTS.TRUNC_1M];
24
+ // =============================================================================
25
+ // WebCrypto Runtime Check
26
+ // =============================================================================
27
+ /**
28
+ * Get WebCrypto subtle interface with explicit runtime check.
29
+ * Prefer globalThis.crypto over bare crypto to avoid bundler ambiguity.
30
+ *
31
+ * @throws Error if WebCrypto is not available
32
+ */
33
+ function getSubtle() {
34
+ const subtle = globalThis.crypto?.subtle;
35
+ if (!subtle) {
36
+ throw new Error('WebCrypto (crypto.subtle) is required but not available. ' +
37
+ 'Ensure you are running in Node.js 18+, Deno, Bun, or a modern browser.');
38
+ }
39
+ return subtle;
40
+ }
41
+ // =============================================================================
42
+ // SHA-256 Helper (Web Crypto API - Runtime Neutral)
43
+ // =============================================================================
44
+ /**
45
+ * Compute SHA-256 hash of bytes using Web Crypto API.
46
+ * Returns lowercase hex string.
47
+ */
48
+ async function sha256Hex(data) {
49
+ const subtle = getSubtle();
50
+ const hashBuffer = await subtle.digest('SHA-256', data);
51
+ const hashArray = new Uint8Array(hashBuffer);
52
+ return Array.from(hashArray)
53
+ .map((b) => b.toString(16).padStart(2, '0'))
54
+ .join('');
55
+ }
56
+ // =============================================================================
57
+ // Action Hasher Implementation
58
+ // =============================================================================
59
+ /**
60
+ * Default hasher implementation using JCS + SHA-256.
61
+ *
62
+ * Determinism guarantees:
63
+ * - Same input bytes -> same digest
64
+ * - Same SpoolEntry (minus entry_digest) -> same chain hash
65
+ * - Truncation algorithm is deterministic (first N bytes)
66
+ *
67
+ * Supported truncation thresholds:
68
+ * - 64k (65536 bytes) -> alg: 'sha-256:trunc-64k'
69
+ * - 1m (1048576 bytes) -> alg: 'sha-256:trunc-1m'
70
+ */
71
+ class ActionHasher {
72
+ truncateThreshold;
73
+ constructor(config = {}) {
74
+ const threshold = config.truncateThreshold ?? types_1.SIZE_CONSTANTS.TRUNC_1M;
75
+ // Validate threshold is one of the supported values
76
+ if (!VALID_TRUNCATE_THRESHOLDS.includes(threshold)) {
77
+ throw new RangeError(`truncateThreshold must be 64k (${types_1.SIZE_CONSTANTS.TRUNC_64K}) or 1m (${types_1.SIZE_CONSTANTS.TRUNC_1M}), ` +
78
+ `got ${threshold}`);
79
+ }
80
+ this.truncateThreshold = threshold;
81
+ // Validate WebCrypto is available at construction time
82
+ getSubtle();
83
+ }
84
+ /**
85
+ * Compute digest for payload bytes.
86
+ * Automatically truncates if payload exceeds threshold.
87
+ */
88
+ async digest(payload) {
89
+ const bytes = payload.length;
90
+ // No truncation needed - full SHA-256
91
+ if (bytes <= this.truncateThreshold) {
92
+ return {
93
+ alg: 'sha-256',
94
+ value: await sha256Hex(payload),
95
+ bytes,
96
+ };
97
+ }
98
+ // Large payload: truncate to threshold
99
+ const truncated = payload.slice(0, this.truncateThreshold);
100
+ // Determine algorithm label based on truncation size
101
+ let alg;
102
+ if (this.truncateThreshold === types_1.SIZE_CONSTANTS.TRUNC_64K) {
103
+ alg = 'sha-256:trunc-64k';
104
+ }
105
+ else {
106
+ // SIZE_CONSTANTS.TRUNC_1M
107
+ alg = 'sha-256:trunc-1m';
108
+ }
109
+ return {
110
+ alg,
111
+ value: await sha256Hex(truncated),
112
+ bytes, // Original size for audit
113
+ };
114
+ }
115
+ /**
116
+ * Compute digest for a spool entry (for chaining).
117
+ * Uses JCS (RFC 8785) for deterministic serialization.
118
+ */
119
+ async digestEntry(entry) {
120
+ // JCS canonicalization ensures deterministic serialization
121
+ const canonical = (0, crypto_1.canonicalize)(entry);
122
+ const bytes = new TextEncoder().encode(canonical);
123
+ return sha256Hex(bytes);
124
+ }
125
+ }
126
+ exports.ActionHasher = ActionHasher;
127
+ /**
128
+ * Create a default hasher instance.
129
+ */
130
+ function createHasher(config) {
131
+ return new ActionHasher(config);
132
+ }
133
+ //# sourceMappingURL=hasher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hasher.js","sourceRoot":"","sources":["../src/hasher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @peac/capture-core
3
+ *
4
+ * Runtime-neutral capture pipeline for PEAC interaction evidence.
5
+ *
6
+ * This package provides:
7
+ * - Types for captured actions and spool entries
8
+ * - Interfaces for storage (SpoolStore) and deduplication (DedupeIndex)
9
+ * - Hasher for deterministic payload hashing
10
+ * - Mapper for converting to InteractionEvidence
11
+ * - CaptureSession for orchestrating the pipeline
12
+ *
13
+ * NO FILESYSTEM OPERATIONS - those belong in @peac/capture-node.
14
+ *
15
+ * For in-memory test implementations, import from '@peac/capture-core/testkit'.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import {
20
+ * createCaptureSession,
21
+ * createHasher,
22
+ * toInteractionEvidence,
23
+ * } from '@peac/capture-core';
24
+ * import {
25
+ * createInMemorySpoolStore,
26
+ * createInMemoryDedupeIndex,
27
+ * } from '@peac/capture-core/testkit';
28
+ *
29
+ * const session = createCaptureSession({
30
+ * store: createInMemorySpoolStore(),
31
+ * dedupe: createInMemoryDedupeIndex(),
32
+ * hasher: createHasher(),
33
+ * });
34
+ *
35
+ * const result = await session.capture({
36
+ * id: 'action-123',
37
+ * kind: 'tool.call',
38
+ * platform: 'my-platform',
39
+ * started_at: new Date().toISOString(),
40
+ * tool_name: 'search',
41
+ * });
42
+ *
43
+ * if (result.success) {
44
+ * const evidence = toInteractionEvidence(result.entry);
45
+ * }
46
+ * ```
47
+ */
48
+ export type { CapturedAction, ActionStatus, PolicySnapshot, SpoolEntry, SpoolStore, DedupeIndex, DedupeEntry, Hasher, HasherConfig, CaptureSession, CaptureSessionConfig, CaptureResult, CaptureErrorCode, SpoolAnchor, } from './types';
49
+ export { GENESIS_DIGEST, SIZE_CONSTANTS } from './types';
50
+ export { ActionHasher, createHasher } from './hasher';
51
+ export { toInteractionEvidence, toInteractionEvidenceBatch } from './mapper';
52
+ export type { MapperOptions } from './mapper';
53
+ export { DefaultCaptureSession, createCaptureSession } from './session';
54
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAMH,YAAY,EAEV,cAAc,EACd,YAAY,EACZ,cAAc,EACd,UAAU,EAGV,UAAU,EACV,WAAW,EACX,WAAW,EACX,MAAM,EACN,YAAY,EACZ,cAAc,EACd,oBAAoB,EAGpB,aAAa,EACb,gBAAgB,EAGhB,WAAW,GACZ,MAAM,SAAS,CAAC;AAMjB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAMzD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAMtD,OAAO,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAC7E,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAM9C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * @peac/capture-core
4
+ *
5
+ * Runtime-neutral capture pipeline for PEAC interaction evidence.
6
+ *
7
+ * This package provides:
8
+ * - Types for captured actions and spool entries
9
+ * - Interfaces for storage (SpoolStore) and deduplication (DedupeIndex)
10
+ * - Hasher for deterministic payload hashing
11
+ * - Mapper for converting to InteractionEvidence
12
+ * - CaptureSession for orchestrating the pipeline
13
+ *
14
+ * NO FILESYSTEM OPERATIONS - those belong in @peac/capture-node.
15
+ *
16
+ * For in-memory test implementations, import from '@peac/capture-core/testkit'.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import {
21
+ * createCaptureSession,
22
+ * createHasher,
23
+ * toInteractionEvidence,
24
+ * } from '@peac/capture-core';
25
+ * import {
26
+ * createInMemorySpoolStore,
27
+ * createInMemoryDedupeIndex,
28
+ * } from '@peac/capture-core/testkit';
29
+ *
30
+ * const session = createCaptureSession({
31
+ * store: createInMemorySpoolStore(),
32
+ * dedupe: createInMemoryDedupeIndex(),
33
+ * hasher: createHasher(),
34
+ * });
35
+ *
36
+ * const result = await session.capture({
37
+ * id: 'action-123',
38
+ * kind: 'tool.call',
39
+ * platform: 'my-platform',
40
+ * started_at: new Date().toISOString(),
41
+ * tool_name: 'search',
42
+ * });
43
+ *
44
+ * if (result.success) {
45
+ * const evidence = toInteractionEvidence(result.entry);
46
+ * }
47
+ * ```
48
+ */
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.createCaptureSession = exports.DefaultCaptureSession = exports.toInteractionEvidenceBatch = exports.toInteractionEvidence = exports.createHasher = exports.ActionHasher = exports.SIZE_CONSTANTS = exports.GENESIS_DIGEST = void 0;
51
+ // =============================================================================
52
+ // Constants (public API)
53
+ // =============================================================================
54
+ var types_1 = require("./types");
55
+ Object.defineProperty(exports, "GENESIS_DIGEST", { enumerable: true, get: function () { return types_1.GENESIS_DIGEST; } });
56
+ Object.defineProperty(exports, "SIZE_CONSTANTS", { enumerable: true, get: function () { return types_1.SIZE_CONSTANTS; } });
57
+ // =============================================================================
58
+ // Hasher (public API)
59
+ // =============================================================================
60
+ var hasher_1 = require("./hasher");
61
+ Object.defineProperty(exports, "ActionHasher", { enumerable: true, get: function () { return hasher_1.ActionHasher; } });
62
+ Object.defineProperty(exports, "createHasher", { enumerable: true, get: function () { return hasher_1.createHasher; } });
63
+ // =============================================================================
64
+ // Mapper (public API)
65
+ // =============================================================================
66
+ var mapper_1 = require("./mapper");
67
+ Object.defineProperty(exports, "toInteractionEvidence", { enumerable: true, get: function () { return mapper_1.toInteractionEvidence; } });
68
+ Object.defineProperty(exports, "toInteractionEvidenceBatch", { enumerable: true, get: function () { return mapper_1.toInteractionEvidenceBatch; } });
69
+ // =============================================================================
70
+ // Session (public API)
71
+ // =============================================================================
72
+ var session_1 = require("./session");
73
+ Object.defineProperty(exports, "DefaultCaptureSession", { enumerable: true, get: function () { return session_1.DefaultCaptureSession; } });
74
+ Object.defineProperty(exports, "createCaptureSession", { enumerable: true, get: function () { return session_1.createCaptureSession; } });
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;;;AA8BH,gFAAgF;AAChF,yBAAyB;AACzB,gFAAgF;AAEhF,iCAAyD;AAAhD,uGAAA,cAAc,OAAA;AAAE,uGAAA,cAAc,OAAA;AAEvC,gFAAgF;AAChF,sBAAsB;AACtB,gFAAgF;AAEhF,mCAAsD;AAA7C,sGAAA,YAAY,OAAA;AAAE,sGAAA,YAAY,OAAA;AAEnC,gFAAgF;AAChF,sBAAsB;AACtB,gFAAgF;AAEhF,mCAA6E;AAApE,+GAAA,qBAAqB,OAAA;AAAE,oHAAA,0BAA0B,OAAA;AAG1D,gFAAgF;AAChF,uBAAuB;AACvB,gFAAgF;AAEhF,qCAAwE;AAA/D,gHAAA,qBAAqB,OAAA;AAAE,+GAAA,oBAAoB,OAAA"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @peac/capture-core - Evidence Mapper
3
+ *
4
+ * Transforms SpoolEntry into InteractionEvidenceV01.
5
+ * This is a pure transformation with no side effects.
6
+ */
7
+ import type { InteractionEvidenceV01 } from '@peac/schema';
8
+ import type { SpoolEntry } from './types';
9
+ /**
10
+ * Options for mapping SpoolEntry to InteractionEvidence.
11
+ */
12
+ export interface MapperOptions {
13
+ /**
14
+ * Default redaction mode for payloads.
15
+ * @default 'hash_only'
16
+ */
17
+ defaultRedaction?: 'hash_only' | 'redacted' | 'plaintext_allowlisted';
18
+ /**
19
+ * Include spool anchor in evidence extensions.
20
+ * @default false
21
+ */
22
+ includeSpoolAnchor?: boolean;
23
+ }
24
+ /**
25
+ * Convert a SpoolEntry to InteractionEvidenceV01.
26
+ *
27
+ * This is a pure, deterministic transformation.
28
+ * The same SpoolEntry will always produce the same InteractionEvidence.
29
+ */
30
+ export declare function toInteractionEvidence(entry: SpoolEntry, options?: MapperOptions): InteractionEvidenceV01;
31
+ /**
32
+ * Batch convert SpoolEntries to InteractionEvidence array.
33
+ */
34
+ export declare function toInteractionEvidenceBatch(entries: SpoolEntry[], options?: MapperOptions): InteractionEvidenceV01[];
35
+ //# sourceMappingURL=mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapper.d.ts","sourceRoot":"","sources":["../src/mapper.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,sBAAsB,EAQvB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,SAAS,CAAC;AAYvD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,uBAAuB,CAAC;IAEtE;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAuID;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,aAAkB,GAC1B,sBAAsB,CAsGxB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,UAAU,EAAE,EACrB,OAAO,GAAE,aAAkB,GAC1B,sBAAsB,EAAE,CAE1B"}