@longarc/mdash 3.1.1 → 3.1.3

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 (178) hide show
  1. package/README.md +86 -23
  2. package/SECURITY.md +254 -0
  3. package/dist/accountability/engine.d.ts +27 -0
  4. package/dist/accountability/engine.d.ts.map +1 -0
  5. package/dist/accountability/engine.js +148 -0
  6. package/dist/accountability/engine.js.map +1 -0
  7. package/dist/accountability/types.d.ts +46 -0
  8. package/dist/accountability/types.d.ts.map +1 -0
  9. package/dist/accountability/types.js +8 -0
  10. package/dist/accountability/types.js.map +1 -0
  11. package/dist/checkpoint/engine.d.ts +2 -2
  12. package/dist/checkpoint/engine.d.ts.map +1 -1
  13. package/dist/checkpoint/engine.js +5 -1
  14. package/dist/checkpoint/engine.js.map +1 -1
  15. package/dist/context/compose.d.ts +62 -0
  16. package/dist/context/compose.d.ts.map +1 -0
  17. package/dist/context/compose.js +286 -0
  18. package/dist/context/compose.js.map +1 -0
  19. package/dist/context/crypto/hash.d.ts +100 -0
  20. package/dist/context/crypto/hash.d.ts.map +1 -0
  21. package/dist/context/crypto/hash.js +248 -0
  22. package/dist/context/crypto/hash.js.map +1 -0
  23. package/dist/context/crypto/hmac.d.ts +80 -0
  24. package/dist/context/crypto/hmac.d.ts.map +1 -0
  25. package/dist/context/crypto/hmac.js +192 -0
  26. package/dist/context/crypto/hmac.js.map +1 -0
  27. package/dist/context/crypto/index.d.ts +7 -0
  28. package/dist/context/crypto/index.d.ts.map +1 -0
  29. package/dist/context/crypto/index.js +7 -0
  30. package/dist/context/crypto/index.js.map +1 -0
  31. package/dist/context/engine-v3.0-backup.d.ts +197 -0
  32. package/dist/context/engine-v3.0-backup.d.ts.map +1 -0
  33. package/dist/context/engine-v3.0-backup.js +392 -0
  34. package/dist/context/engine-v3.0-backup.js.map +1 -0
  35. package/dist/context/engine.d.ts +2 -2
  36. package/dist/context/engine.d.ts.map +1 -1
  37. package/dist/context/engine.js +2 -2
  38. package/dist/context/engine.js.map +1 -1
  39. package/dist/context/fragment.d.ts +99 -0
  40. package/dist/context/fragment.d.ts.map +1 -0
  41. package/dist/context/fragment.js +316 -0
  42. package/dist/context/fragment.js.map +1 -0
  43. package/dist/context/index.d.ts +99 -0
  44. package/dist/context/index.d.ts.map +1 -0
  45. package/dist/context/index.js +180 -0
  46. package/dist/context/index.js.map +1 -0
  47. package/dist/context/provenance.d.ts +80 -0
  48. package/dist/context/provenance.d.ts.map +1 -0
  49. package/dist/context/provenance.js +294 -0
  50. package/dist/context/provenance.js.map +1 -0
  51. package/dist/context/resolve.d.ts +106 -0
  52. package/dist/context/resolve.d.ts.map +1 -0
  53. package/dist/context/resolve.js +440 -0
  54. package/dist/context/resolve.js.map +1 -0
  55. package/dist/context/store.d.ts +156 -0
  56. package/dist/context/store.d.ts.map +1 -0
  57. package/dist/context/store.js +396 -0
  58. package/dist/context/store.js.map +1 -0
  59. package/dist/context/types.d.ts +463 -0
  60. package/dist/context/types.d.ts.map +1 -0
  61. package/dist/context/types.js +94 -0
  62. package/dist/context/types.js.map +1 -0
  63. package/dist/context/utils/atomic.d.ts +76 -0
  64. package/dist/context/utils/atomic.d.ts.map +1 -0
  65. package/dist/context/utils/atomic.js +159 -0
  66. package/dist/context/utils/atomic.js.map +1 -0
  67. package/dist/context/utils/credit.d.ts +65 -0
  68. package/dist/context/utils/credit.d.ts.map +1 -0
  69. package/dist/context/utils/credit.js +164 -0
  70. package/dist/context/utils/credit.js.map +1 -0
  71. package/dist/context/utils/index.d.ts +13 -0
  72. package/dist/context/utils/index.d.ts.map +1 -0
  73. package/dist/context/utils/index.js +13 -0
  74. package/dist/context/utils/index.js.map +1 -0
  75. package/dist/context/utils/utility.d.ts +63 -0
  76. package/dist/context/utils/utility.d.ts.map +1 -0
  77. package/dist/context/utils/utility.js +141 -0
  78. package/dist/context/utils/utility.js.map +1 -0
  79. package/dist/core/commitment.d.ts +26 -3
  80. package/dist/core/commitment.d.ts.map +1 -1
  81. package/dist/core/commitment.js +45 -7
  82. package/dist/core/commitment.js.map +1 -1
  83. package/dist/core/crypto.d.ts +2 -0
  84. package/dist/core/crypto.d.ts.map +1 -1
  85. package/dist/core/crypto.js +12 -0
  86. package/dist/core/crypto.js.map +1 -1
  87. package/dist/index.d.ts +11 -6
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +35 -10
  90. package/dist/index.js.map +1 -1
  91. package/dist/mcca/engine.d.ts.map +1 -1
  92. package/dist/mcca/engine.js +5 -4
  93. package/dist/mcca/engine.js.map +1 -1
  94. package/dist/physics/engine.d.ts +3 -2
  95. package/dist/physics/engine.d.ts.map +1 -1
  96. package/dist/physics/engine.js +37 -3
  97. package/dist/physics/engine.js.map +1 -1
  98. package/dist/provenance/api-handler.d.ts +45 -0
  99. package/dist/provenance/api-handler.d.ts.map +1 -0
  100. package/dist/provenance/api-handler.js +223 -0
  101. package/dist/provenance/api-handler.js.map +1 -0
  102. package/dist/provenance/api-types.d.ts +108 -0
  103. package/dist/provenance/api-types.d.ts.map +1 -0
  104. package/dist/provenance/api-types.js +9 -0
  105. package/dist/provenance/api-types.js.map +1 -0
  106. package/dist/provenance/index.d.ts +6 -0
  107. package/dist/provenance/index.d.ts.map +1 -0
  108. package/dist/provenance/index.js +3 -0
  109. package/dist/provenance/index.js.map +1 -0
  110. package/dist/provenance/provenance-engine.d.ts +63 -0
  111. package/dist/provenance/provenance-engine.d.ts.map +1 -0
  112. package/dist/provenance/provenance-engine.js +311 -0
  113. package/dist/provenance/provenance-engine.js.map +1 -0
  114. package/dist/provenance/types.d.ts +193 -0
  115. package/dist/provenance/types.d.ts.map +1 -0
  116. package/dist/provenance/types.js +9 -0
  117. package/dist/provenance/types.js.map +1 -0
  118. package/dist/tee/engine.d.ts.map +1 -1
  119. package/dist/tee/engine.js +14 -0
  120. package/dist/tee/engine.js.map +1 -1
  121. package/dist/warrant/engine.d.ts +24 -1
  122. package/dist/warrant/engine.d.ts.map +1 -1
  123. package/dist/warrant/engine.js +76 -1
  124. package/dist/warrant/engine.js.map +1 -1
  125. package/dist/zk/engine.d.ts.map +1 -1
  126. package/dist/zk/engine.js +7 -4
  127. package/dist/zk/engine.js.map +1 -1
  128. package/docs/SECURITY-PATCHES.md +170 -0
  129. package/package.json +17 -5
  130. package/src/__tests__/accountability.test.ts +308 -0
  131. package/src/__tests__/l1-verification-modes.test.ts +424 -0
  132. package/src/__tests__/phase1.benchmark.test.ts +94 -0
  133. package/src/__tests__/phase1.test.ts +0 -77
  134. package/src/__tests__/phase2-4.benchmark.test.ts +60 -0
  135. package/src/__tests__/phase2-4.test.ts +1 -52
  136. package/src/__tests__/provenance/api-handler.test.ts +356 -0
  137. package/src/__tests__/provenance/provenance-engine.test.ts +628 -0
  138. package/src/__tests__/sa-2026-008.test.ts +45 -0
  139. package/src/__tests__/sa-2026-009.test.ts +86 -0
  140. package/src/__tests__/sa-2026-010.test.ts +72 -0
  141. package/src/__tests__/sa-2026-012.test.ts +65 -0
  142. package/src/__tests__/sa-2026-nfc.test.ts +40 -0
  143. package/src/__tests__/security.test.ts +786 -0
  144. package/src/accountability/engine.ts +230 -0
  145. package/src/accountability/types.ts +58 -0
  146. package/src/checkpoint/engine.ts +6 -2
  147. package/src/context/__tests__/caret-v0.2.0.test.ts +860 -0
  148. package/src/context/__tests__/integration.test.ts +356 -0
  149. package/src/context/compose.ts +388 -0
  150. package/src/context/crypto/hash.ts +277 -0
  151. package/src/context/crypto/hmac.ts +253 -0
  152. package/src/context/crypto/index.ts +29 -0
  153. package/src/context/engine-v3.0-backup.ts +598 -0
  154. package/src/context/engine.ts +2 -2
  155. package/src/context/fragment.ts +454 -0
  156. package/src/context/index.ts +427 -0
  157. package/src/context/provenance.ts +380 -0
  158. package/src/context/resolve.ts +581 -0
  159. package/src/context/store.ts +503 -0
  160. package/src/context/types.ts +679 -0
  161. package/src/context/utils/atomic.ts +207 -0
  162. package/src/context/utils/credit.ts +224 -0
  163. package/src/context/utils/index.ts +13 -0
  164. package/src/context/utils/utility.ts +200 -0
  165. package/src/core/commitment.ts +130 -68
  166. package/src/core/crypto.ts +13 -0
  167. package/src/index.ts +62 -10
  168. package/src/mcca/engine.ts +5 -4
  169. package/src/physics/engine.ts +42 -5
  170. package/src/provenance/api-handler.ts +248 -0
  171. package/src/provenance/api-types.ts +112 -0
  172. package/src/provenance/index.ts +19 -0
  173. package/src/provenance/provenance-engine.ts +387 -0
  174. package/src/provenance/types.ts +211 -0
  175. package/src/tee/engine.ts +16 -0
  176. package/src/warrant/engine.ts +89 -1
  177. package/src/zk/engine.ts +8 -4
  178. package/tsconfig.json +1 -1
@@ -0,0 +1,860 @@
1
+ /**
2
+ * Caret Engine v0.2.0 Test Suite
3
+ * Now integrated as mdash Context Module v3.1
4
+ *
5
+ * Comprehensive tests for research pattern integration:
6
+ * - Core runtime (seal, compose, resolve)
7
+ * - Utility learning
8
+ * - Credit assignment
9
+ * - Keyword search (Three-Layer Index Layer 2)
10
+ * - Semantic similarity (Three-Layer Index Layer 1)
11
+ * - Atomic encoding utilities
12
+ */
13
+
14
+ import { describe, test, expect, beforeEach } from 'vitest';
15
+
16
+ import {
17
+ createCaret,
18
+ MemoryStore,
19
+ ExtendedMemoryStore,
20
+ VERSION,
21
+ // Crypto
22
+ sha256,
23
+ sha256Object,
24
+ hmacSeal,
25
+ hmacVerify,
26
+ // Types
27
+ type ContextFragment,
28
+ type ExecutionTrace,
29
+ type InfluenceBudget,
30
+ type UtilityScore,
31
+ type Timestamp,
32
+ type FragmentId,
33
+ // Constraint builders
34
+ maxAge,
35
+ requireTrust,
36
+ requireInfluence,
37
+ requireMCCA,
38
+ // Utility learning
39
+ updateUtilityScore,
40
+ createInitialUtility,
41
+ predictSuccess,
42
+ // Credit assignment
43
+ assignCredit,
44
+ validateCreditAssignment,
45
+ attributeLiability,
46
+ validateLiabilityShares,
47
+ createDefaultInfluenceBudget,
48
+ isMCCACompliant,
49
+ normalizeInfluenceBudget,
50
+ // Atomic encoding
51
+ resolveCoreferences,
52
+ createDefaultContext,
53
+ hasUnresolvedCoreferences,
54
+ canonicalEncode,
55
+ isValidSubject,
56
+ isValidTimestamp,
57
+ // Type guards
58
+ isValidInfluenceBudget,
59
+ sumInfluence,
60
+ } from '../index.js';
61
+
62
+ // ============================================================================
63
+ // TEST UTILITIES
64
+ // ============================================================================
65
+
66
+ const SEAL_KEY = 'test-secret-key-do-not-use-in-production-32';
67
+
68
+ function createTestTrace(steps: number = 3): ExecutionTrace {
69
+ const traceSteps = [];
70
+ for (let i = 0; i < steps; i++) {
71
+ traceSteps.push({
72
+ step_id: `Agent_${i.toString().padStart(4, '0')}_step_${i}`,
73
+ timestamp: new Date(Date.now() + i * 1000).toISOString() as Timestamp,
74
+ action: 'INVOKE' as const,
75
+ observation_hash: `hash_${i}`,
76
+ influence_consumed: {
77
+ system: 0.1,
78
+ user: 0.05,
79
+ environment: 0.03,
80
+ assistant: 0.02,
81
+ },
82
+ });
83
+ }
84
+
85
+ return {
86
+ trace_id: `trace_${Date.now()}` as FragmentId,
87
+ steps: traceSteps,
88
+ outcome: 'success',
89
+ terminal_state: 'completed',
90
+ };
91
+ }
92
+
93
+ // ============================================================================
94
+ // VERSION & BASIC TESTS
95
+ // ============================================================================
96
+
97
+ describe('Caret Core', () => {
98
+ test('VERSION is 0.2.0', () => {
99
+ expect(VERSION).toBe('3.1.0');
100
+ });
101
+
102
+ test('createCaret requires seal_key', () => {
103
+ expect(() => createCaret({ seal_key: '' })).toThrow();
104
+ });
105
+
106
+ test('createCaret enforces minimum key length', () => {
107
+ expect(() => createCaret({ seal_key: 'short' })).toThrow('at least 32');
108
+ });
109
+
110
+ test('createCaret creates runtime with valid key', () => {
111
+ const caret = createCaret({ seal_key: SEAL_KEY });
112
+ expect(caret).toBeDefined();
113
+ expect(caret.seal).toBeDefined();
114
+ expect(caret.compose).toBeDefined();
115
+ expect(caret.resolve).toBeDefined();
116
+ expect(caret.store).toBeDefined();
117
+ });
118
+ });
119
+
120
+ // ============================================================================
121
+ // CRYPTO TESTS
122
+ // ============================================================================
123
+
124
+ describe('Crypto', () => {
125
+ test('sha256 produces consistent hashes', async () => {
126
+ const hash1 = await sha256('hello world');
127
+ const hash2 = await sha256('hello world');
128
+ expect(hash1).toBe(hash2);
129
+ expect(hash1.length).toBe(64);
130
+ });
131
+
132
+ test('sha256 produces different hashes for different inputs', async () => {
133
+ const hash1 = await sha256('hello');
134
+ const hash2 = await sha256('world');
135
+ expect(hash1).not.toBe(hash2);
136
+ });
137
+
138
+ test('sha256Object is deterministic with sorted keys', async () => {
139
+ const hash1 = await sha256Object({ b: 2, a: 1 });
140
+ const hash2 = await sha256Object({ a: 1, b: 2 });
141
+ expect(hash1).toBe(hash2);
142
+ });
143
+
144
+ test('hmacSeal produces verifiable seals', async () => {
145
+ const content = { message: 'test' };
146
+ const seal = await hmacSeal(content, SEAL_KEY);
147
+ const isValid = await hmacVerify(content, seal, SEAL_KEY);
148
+ expect(isValid).toBe(true);
149
+ });
150
+
151
+ test('hmacVerify rejects tampered content', async () => {
152
+ const content = { message: 'test' };
153
+ const seal = await hmacSeal(content, SEAL_KEY);
154
+ const tampered = { message: 'tampered' };
155
+ const isValid = await hmacVerify(tampered, seal, SEAL_KEY);
156
+ expect(isValid).toBe(false);
157
+ });
158
+ });
159
+
160
+ // ============================================================================
161
+ // FRAGMENT TESTS
162
+ // ============================================================================
163
+
164
+ describe('Fragment Operations', () => {
165
+ let caret: ReturnType<typeof createCaret>;
166
+
167
+ beforeEach(() => {
168
+ caret = createCaret({ seal_key: SEAL_KEY });
169
+ });
170
+
171
+ test('seal creates valid fragment', async () => {
172
+ const fragment = await caret.seal({
173
+ content: { message: 'hello' },
174
+ source: 'test://example' as any,
175
+ attribution: 'system',
176
+ });
177
+
178
+ expect(fragment.id).toBeDefined();
179
+ expect(fragment.hash).toBeDefined();
180
+ expect(fragment.seal).toBeDefined();
181
+ expect(fragment.content.data).toEqual({ message: 'hello' });
182
+ expect(fragment.version).toBe('v3.1');
183
+ });
184
+
185
+ test('seal with trust level', async () => {
186
+ const fragment = await caret.seal({
187
+ content: 'test',
188
+ source: 'test://example' as any,
189
+ attribution: 'user',
190
+ trust_level: 80 as any,
191
+ });
192
+
193
+ expect(fragment.provenance.head.trust_level).toBe(80);
194
+ });
195
+
196
+ test('verify returns true for valid fragment', async () => {
197
+ const fragment = await caret.seal({
198
+ content: 'verify test',
199
+ source: 'test://verify' as any,
200
+ attribution: 'system',
201
+ });
202
+
203
+ const isValid = await caret.verify(fragment);
204
+ expect(isValid).toBe(true);
205
+ });
206
+ });
207
+
208
+ // ============================================================================
209
+ // CONSTRAINT TESTS
210
+ // ============================================================================
211
+
212
+ describe('Constraints', () => {
213
+ let caret: ReturnType<typeof createCaret>;
214
+
215
+ beforeEach(() => {
216
+ caret = createCaret({ seal_key: SEAL_KEY });
217
+ });
218
+
219
+ test('maxAge constraint', async () => {
220
+ const fragment = await caret.seal({
221
+ content: 'age test',
222
+ source: 'test://age' as any,
223
+ attribution: 'system',
224
+ constraints: [maxAge(60000)],
225
+ });
226
+
227
+ const result = await caret.resolve(fragment);
228
+ expect(result.success).toBe(true);
229
+ });
230
+
231
+ test('requireTrust constraint', async () => {
232
+ const fragment = await caret.seal({
233
+ content: 'trust test',
234
+ source: 'test://trust' as any,
235
+ attribution: 'system',
236
+ trust_level: 90 as any,
237
+ constraints: [requireTrust(80)],
238
+ });
239
+
240
+ const result = await caret.resolve(fragment);
241
+ expect(result.success).toBe(true);
242
+ });
243
+
244
+ test('requireInfluence constraint', async () => {
245
+ const budget: InfluenceBudget = {
246
+ system: 0.45,
247
+ user: 0.30,
248
+ environment: 0.15,
249
+ assistant: 0.10,
250
+ };
251
+
252
+ const constraint = requireInfluence(budget);
253
+ expect(constraint.kind).toBe('influence');
254
+ expect(constraint.budget).toEqual(budget);
255
+ });
256
+
257
+ test('requireMCCA constraint creates MCCA-compliant budget', () => {
258
+ const constraint = requireMCCA();
259
+ expect(constraint.kind).toBe('influence');
260
+ expect(isMCCACompliant(constraint.budget)).toBe(true);
261
+ });
262
+ });
263
+
264
+ // ============================================================================
265
+ // UTILITY LEARNING TESTS
266
+ // ============================================================================
267
+
268
+ describe('Utility Learning', () => {
269
+ test('createInitialUtility creates valid utility score', () => {
270
+ const utility = createInitialUtility();
271
+ expect(utility.score).toBe(0);
272
+ expect(utility.confidence).toBe(0);
273
+ expect(utility.update_count).toBe(0);
274
+ });
275
+
276
+ test('updateUtilityScore increments correctly', () => {
277
+ const initial = createInitialUtility();
278
+ const feedback = {
279
+ reward: 1.0,
280
+ source: 'human' as const,
281
+ timestamp: new Date().toISOString() as Timestamp,
282
+ };
283
+
284
+ const updated = updateUtilityScore(initial, feedback);
285
+
286
+ expect(updated.score).toBeGreaterThan(0);
287
+ expect(updated.update_count).toBe(1);
288
+ expect(updated.confidence).toBeGreaterThan(0);
289
+ });
290
+
291
+ test('updateUtilityScore bounds score to [-1, 1]', () => {
292
+ let utility = createInitialUtility();
293
+
294
+ // Push score high
295
+ for (let i = 0; i < 20; i++) {
296
+ utility = updateUtilityScore(utility, {
297
+ reward: 1.0,
298
+ source: 'system' as const,
299
+ timestamp: new Date().toISOString() as Timestamp,
300
+ });
301
+ }
302
+
303
+ expect(utility.score).toBeLessThanOrEqual(1.0);
304
+ expect(utility.score).toBeGreaterThanOrEqual(-1.0);
305
+ });
306
+
307
+ test('updateUtilityScore increases confidence with updates', () => {
308
+ let utility = createInitialUtility();
309
+ const confidences: number[] = [utility.confidence];
310
+
311
+ for (let i = 0; i < 10; i++) {
312
+ utility = updateUtilityScore(utility, {
313
+ reward: 0.5,
314
+ source: 'environment' as const,
315
+ timestamp: new Date().toISOString() as Timestamp,
316
+ });
317
+ confidences.push(utility.confidence);
318
+ }
319
+
320
+ // Each confidence should be >= previous
321
+ for (let i = 1; i < confidences.length; i++) {
322
+ expect(confidences[i]).toBeGreaterThanOrEqual(confidences[i-1]!);
323
+ }
324
+ });
325
+
326
+ test('predictSuccess returns prior for empty history', () => {
327
+ const prediction = predictSuccess([]);
328
+ expect(prediction).toBe(0.5);
329
+ });
330
+ });
331
+
332
+ // ============================================================================
333
+ // CREDIT ASSIGNMENT TESTS
334
+ // ============================================================================
335
+
336
+ describe('Credit Assignment', () => {
337
+ test('assignCredit distributes terminal reward', () => {
338
+ const trace = createTestTrace(3);
339
+ const terminalReward = 1.0;
340
+
341
+ const assignment = assignCredit(trace, terminalReward);
342
+
343
+ expect(assignment.trace_id).toBe(trace.trace_id);
344
+ expect(assignment.terminal_reward).toBe(terminalReward);
345
+ expect(assignment.step_credits.length).toBe(3);
346
+ });
347
+
348
+ test('assignCredit conserves terminal reward', () => {
349
+ const trace = createTestTrace(5);
350
+ const terminalReward = 10.0;
351
+
352
+ const assignment = assignCredit(trace, terminalReward);
353
+
354
+ expect(validateCreditAssignment(assignment)).toBe(true);
355
+ });
356
+
357
+ test('assignCredit proportional to influence', () => {
358
+ // Create trace with unequal influence
359
+ const trace: ExecutionTrace = {
360
+ trace_id: 'test_trace' as FragmentId,
361
+ steps: [
362
+ {
363
+ step_id: 'Agent_0001_step_0',
364
+ timestamp: new Date().toISOString() as Timestamp,
365
+ action: 'INVOKE',
366
+ observation_hash: 'hash_0',
367
+ influence_consumed: { system: 0.4, user: 0.3, environment: 0.2, assistant: 0.1 },
368
+ },
369
+ {
370
+ step_id: 'Agent_0002_step_1',
371
+ timestamp: new Date().toISOString() as Timestamp,
372
+ action: 'CHECKPOINT',
373
+ observation_hash: 'hash_1',
374
+ influence_consumed: { system: 0.1, user: 0.05, environment: 0.03, assistant: 0.02 },
375
+ },
376
+ ],
377
+ outcome: 'success',
378
+ terminal_state: 'done',
379
+ };
380
+
381
+ const assignment = assignCredit(trace, 1.0);
382
+
383
+ // First step has more influence, should get more credit
384
+ expect(assignment.step_credits[0]!.credit).toBeGreaterThan(assignment.step_credits[1]!.credit);
385
+ });
386
+
387
+ test('attributeLiability groups by agent', () => {
388
+ const trace: ExecutionTrace = {
389
+ trace_id: 'liability_trace' as FragmentId,
390
+ steps: [
391
+ {
392
+ step_id: 'Agent_aaa1_step_0',
393
+ timestamp: new Date().toISOString() as Timestamp,
394
+ action: 'INVOKE',
395
+ observation_hash: 'h0',
396
+ influence_consumed: { system: 0.2, user: 0.1, environment: 0.1, assistant: 0.1 },
397
+ },
398
+ {
399
+ step_id: 'Agent_bbb2_step_1',
400
+ timestamp: new Date().toISOString() as Timestamp,
401
+ action: 'CHECKPOINT',
402
+ observation_hash: 'h1',
403
+ influence_consumed: { system: 0.2, user: 0.1, environment: 0.1, assistant: 0.1 },
404
+ },
405
+ {
406
+ step_id: 'Agent_aaa1_step_2',
407
+ timestamp: new Date().toISOString() as Timestamp,
408
+ action: 'TERMINATE',
409
+ observation_hash: 'h2',
410
+ influence_consumed: { system: 0.2, user: 0.1, environment: 0.1, assistant: 0.1 },
411
+ },
412
+ ],
413
+ outcome: 'success',
414
+ terminal_state: 'done',
415
+ };
416
+
417
+ const liability = attributeLiability(trace);
418
+
419
+ // Should have 2 agents
420
+ expect(liability.length).toBe(2);
421
+ expect(validateLiabilityShares(liability)).toBe(true);
422
+
423
+ // Agent_aaa1 should have more liability (2 steps vs 1)
424
+ const agentA = liability.find(l => l.agent_id.includes('aaa1'));
425
+ const agentB = liability.find(l => l.agent_id.includes('bbb2'));
426
+ expect(agentA!.contribution).toBeGreaterThan(agentB!.contribution);
427
+ });
428
+ });
429
+
430
+ // ============================================================================
431
+ // INFLUENCE BUDGET TESTS
432
+ // ============================================================================
433
+
434
+ describe('Influence Budget', () => {
435
+ test('createDefaultInfluenceBudget creates valid budget', () => {
436
+ const budget = createDefaultInfluenceBudget();
437
+ expect(isValidInfluenceBudget(budget)).toBe(true);
438
+ expect(isMCCACompliant(budget)).toBe(true);
439
+ });
440
+
441
+ test('isMCCACompliant validates bounds', () => {
442
+ // Valid
443
+ expect(isMCCACompliant({ system: 0.45, user: 0.30, environment: 0.15, assistant: 0.10 })).toBe(true);
444
+
445
+ // Invalid: system too low
446
+ expect(isMCCACompliant({ system: 0.35, user: 0.30, environment: 0.20, assistant: 0.15 })).toBe(false);
447
+
448
+ // Invalid: user too high
449
+ expect(isMCCACompliant({ system: 0.40, user: 0.40, environment: 0.10, assistant: 0.10 })).toBe(false);
450
+ });
451
+
452
+ test('normalizeInfluenceBudget sums to 1.0', () => {
453
+ const unnormalized = { system: 0.8, user: 0.4, environment: 0.2, assistant: 0.1 };
454
+ const normalized = normalizeInfluenceBudget(unnormalized);
455
+
456
+ const sum = sumInfluence(normalized);
457
+ expect(Math.abs(sum - 1.0)).toBeLessThan(0.001);
458
+ });
459
+
460
+ test('sumInfluence computes total', () => {
461
+ const budget = { system: 0.4, user: 0.3, environment: 0.2, assistant: 0.1 };
462
+ expect(sumInfluence(budget)).toBeCloseTo(1.0);
463
+ });
464
+ });
465
+
466
+ // ============================================================================
467
+ // ATOMIC ENCODING TESTS
468
+ // ============================================================================
469
+
470
+ describe('Atomic Encoding', () => {
471
+ test('resolveCoreferences replaces pronouns', () => {
472
+ const context = createDefaultContext();
473
+ context.lastAgentId = 'Agent_abc123';
474
+
475
+ const text = 'He approved the request';
476
+ const resolved = resolveCoreferences(text, context);
477
+
478
+ expect(resolved).toContain('Agent_abc123');
479
+ });
480
+
481
+ test('resolveCoreferences replaces relative time', () => {
482
+ const context = createDefaultContext();
483
+ const refDate = new Date('2026-01-18T12:00:00Z');
484
+ context.referenceTime = refDate.toISOString();
485
+
486
+ const text = 'Approved yesterday';
487
+ const resolved = resolveCoreferences(text, context);
488
+
489
+ // Should contain ISO timestamp, not "yesterday"
490
+ expect(resolved).not.toContain('yesterday');
491
+ expect(resolved).toContain('2026-01-17');
492
+ });
493
+
494
+ test('hasUnresolvedCoreferences detects pronouns', () => {
495
+ expect(hasUnresolvedCoreferences('He did it')).toBe(true);
496
+ expect(hasUnresolvedCoreferences('Agent_123 invoked Warrant_456')).toBe(false);
497
+ });
498
+
499
+ test('canonicalEncode produces deterministic output', () => {
500
+ const attestation = {
501
+ subject: 'Agent_abc',
502
+ action: 'INVOKE' as const,
503
+ timestamp: '2026-01-18T00:00:00Z',
504
+ };
505
+
506
+ const encoded1 = canonicalEncode(attestation);
507
+ const encoded2 = canonicalEncode(attestation);
508
+
509
+ expect(encoded1).toBe(encoded2);
510
+ });
511
+
512
+ test('isValidSubject validates entity IDs', () => {
513
+ expect(isValidSubject('Agent_abc123')).toBe(true);
514
+ expect(isValidSubject('Warrant_def456')).toBe(true);
515
+ expect(isValidSubject('Checkpoint_abc789')).toBe(true); // hex only
516
+ expect(isValidSubject('User_012abc')).toBe(true);
517
+ expect(isValidSubject('invalid')).toBe(false);
518
+ expect(isValidSubject('Agent_xyz')).toBe(false); // xyz not hex
519
+ });
520
+
521
+ test('isValidTimestamp validates ISO 8601', () => {
522
+ expect(isValidTimestamp('2026-01-18T12:00:00Z')).toBe(true);
523
+ expect(isValidTimestamp('2026-01-18T12:00:00.123Z')).toBe(true);
524
+ expect(isValidTimestamp('yesterday')).toBe(false);
525
+ expect(isValidTimestamp('2026-01-18')).toBe(false);
526
+ });
527
+ });
528
+
529
+ // ============================================================================
530
+ // KEYWORD SEARCH TESTS (Three-Layer Index Layer 2)
531
+ // ============================================================================
532
+
533
+ describe('Keyword Search (Layer 2)', () => {
534
+ let store: MemoryStore;
535
+ let caret: ReturnType<typeof createCaret>;
536
+
537
+ beforeEach(() => {
538
+ store = new MemoryStore(SEAL_KEY);
539
+ caret = createCaret({ seal_key: SEAL_KEY, store });
540
+ });
541
+
542
+ test('queryByKeywords finds matching fragments', async () => {
543
+ await caret.seal({
544
+ content: 'The quick brown fox jumps over the lazy dog',
545
+ source: 'test://keyword1' as any,
546
+ attribution: 'system',
547
+ });
548
+
549
+ await caret.seal({
550
+ content: 'A fast red cat sleeps on the mat',
551
+ source: 'test://keyword2' as any,
552
+ attribution: 'system',
553
+ });
554
+
555
+ const results = await store.queryByKeywords(['fox']);
556
+ expect(results.length).toBe(1);
557
+ expect((results[0]!.content.data as string)).toContain('fox');
558
+ });
559
+
560
+ test('queryByKeywords with matchMode any', async () => {
561
+ await caret.seal({
562
+ content: 'Document about cats and dogs',
563
+ source: 'test://any1' as any,
564
+ attribution: 'system',
565
+ });
566
+
567
+ await caret.seal({
568
+ content: 'Document about birds',
569
+ source: 'test://any2' as any,
570
+ attribution: 'system',
571
+ });
572
+
573
+ const results = await store.queryByKeywords(['cats', 'birds'], 'any');
574
+ expect(results.length).toBe(2);
575
+ });
576
+
577
+ test('queryByKeywords with matchMode all', async () => {
578
+ await caret.seal({
579
+ content: 'Document about cats and dogs',
580
+ source: 'test://all1' as any,
581
+ attribution: 'system',
582
+ });
583
+
584
+ await caret.seal({
585
+ content: 'Document about dogs only',
586
+ source: 'test://all2' as any,
587
+ attribution: 'system',
588
+ });
589
+
590
+ const results = await store.queryByKeywords(['cats', 'dogs'], 'all');
591
+ expect(results.length).toBe(1);
592
+ expect((results[0]!.content.data as string)).toContain('cats');
593
+ expect((results[0]!.content.data as string)).toContain('dogs');
594
+ });
595
+
596
+ test('queryByKeywords is case-insensitive', async () => {
597
+ await caret.seal({
598
+ content: 'UPPERCASE CONTENT',
599
+ source: 'test://case' as any,
600
+ attribution: 'system',
601
+ });
602
+
603
+ const results = await store.queryByKeywords(['uppercase']);
604
+ expect(results.length).toBe(1);
605
+ });
606
+
607
+ test('queryByKeywords searches object content', async () => {
608
+ await caret.seal({
609
+ content: { title: 'Meeting Notes', body: 'Discussed project alpha' },
610
+ source: 'test://object' as any,
611
+ attribution: 'system',
612
+ });
613
+
614
+ const results = await store.queryByKeywords(['alpha']);
615
+ expect(results.length).toBe(1);
616
+ });
617
+
618
+ test('queryByKeywords returns empty for no matches', async () => {
619
+ await caret.seal({
620
+ content: 'Some unrelated content',
621
+ source: 'test://nomatch' as any,
622
+ attribution: 'system',
623
+ });
624
+
625
+ const results = await store.queryByKeywords(['xyz123']);
626
+ expect(results.length).toBe(0);
627
+ });
628
+ });
629
+
630
+ // ============================================================================
631
+ // SEMANTIC SIMILARITY TESTS (Three-Layer Index Layer 1)
632
+ // ============================================================================
633
+
634
+ describe('Semantic Similarity (Layer 1)', () => {
635
+ let store: ExtendedMemoryStore;
636
+ let caret: ReturnType<typeof createCaret>;
637
+
638
+ beforeEach(() => {
639
+ store = new ExtendedMemoryStore(SEAL_KEY);
640
+ caret = createCaret({ seal_key: SEAL_KEY, store });
641
+ });
642
+
643
+ test('ExtendedMemoryStore inherits MemoryStore methods', async () => {
644
+ const fragment = await caret.seal({
645
+ content: 'test content',
646
+ source: 'test://inherit' as any,
647
+ attribution: 'system',
648
+ });
649
+
650
+ const retrieved = await store.get(fragment.id);
651
+ expect(retrieved).not.toBeNull();
652
+ expect(retrieved!.id).toBe(fragment.id);
653
+ });
654
+
655
+ test('setEmbedding stores embedding for fragment', async () => {
656
+ const fragment = await caret.seal({
657
+ content: 'semantic test',
658
+ source: 'test://embed' as any,
659
+ attribution: 'system',
660
+ });
661
+
662
+ const embedding = new Float32Array([0.1, 0.2, 0.3, 0.4]);
663
+ store.setEmbedding(fragment.id, embedding);
664
+
665
+ const stats = store.embeddingStats();
666
+ expect(stats.count).toBe(1);
667
+ });
668
+
669
+ test('queryBySimilarity finds similar fragments', async () => {
670
+ const fragment1 = await caret.seal({
671
+ content: 'machine learning is great',
672
+ source: 'test://sim1' as any,
673
+ attribution: 'system',
674
+ });
675
+
676
+ const fragment2 = await caret.seal({
677
+ content: 'cooking recipes for dinner',
678
+ source: 'test://sim2' as any,
679
+ attribution: 'system',
680
+ });
681
+
682
+ // Manually set embeddings (in real use, embedding function would do this)
683
+ const embedding1 = new Float32Array([0.9, 0.1, 0.0, 0.0]); // ML-like
684
+ const embedding2 = new Float32Array([0.0, 0.0, 0.9, 0.1]); // Cooking-like
685
+
686
+ store.setEmbedding(fragment1.id, embedding1);
687
+ store.setEmbedding(fragment2.id, embedding2);
688
+
689
+ // Query for ML-like content
690
+ const queryEmbedding = new Float32Array([0.8, 0.2, 0.0, 0.0]);
691
+ const results = await store.queryBySimilarity(queryEmbedding, 0.5, 10);
692
+
693
+ expect(results.length).toBeGreaterThan(0);
694
+ expect(results[0]!.fragment.id).toBe(fragment1.id); // ML doc should be first
695
+ expect(results[0]!.similarity).toBeGreaterThan(0.5);
696
+ });
697
+
698
+ test('queryBySimilarity respects threshold', async () => {
699
+ const fragment = await caret.seal({
700
+ content: 'test threshold',
701
+ source: 'test://threshold' as any,
702
+ attribution: 'system',
703
+ });
704
+
705
+ const embedding = new Float32Array([0.1, 0.0, 0.0, 0.9]);
706
+ store.setEmbedding(fragment.id, embedding);
707
+
708
+ // Query with orthogonal vector
709
+ const queryEmbedding = new Float32Array([0.9, 0.1, 0.0, 0.0]);
710
+
711
+ // High threshold should exclude
712
+ const highThreshold = await store.queryBySimilarity(queryEmbedding, 0.9, 10);
713
+ expect(highThreshold.length).toBe(0);
714
+
715
+ // Low threshold should include
716
+ const lowThreshold = await store.queryBySimilarity(queryEmbedding, 0.0, 10);
717
+ expect(lowThreshold.length).toBe(1);
718
+ });
719
+
720
+ test('queryBySimilarity respects limit', async () => {
721
+ // Create multiple fragments
722
+ for (let i = 0; i < 5; i++) {
723
+ const fragment = await caret.seal({
724
+ content: `document ${i}`,
725
+ source: `test://limit${i}` as any,
726
+ attribution: 'system',
727
+ });
728
+
729
+ // All similar embeddings
730
+ const embedding = new Float32Array([0.5, 0.5, 0.0, 0.0]);
731
+ store.setEmbedding(fragment.id, embedding);
732
+ }
733
+
734
+ const queryEmbedding = new Float32Array([0.5, 0.5, 0.0, 0.0]);
735
+ const results = await store.queryBySimilarity(queryEmbedding, 0.0, 3);
736
+
737
+ expect(results.length).toBe(3);
738
+ });
739
+
740
+ test('setEmbeddingFunction auto-embeds on put', async () => {
741
+ // Track calls to embedding function
742
+ let callCount = 0;
743
+ const mockEmbedFn = async (_text: string): Promise<Float32Array> => {
744
+ callCount++;
745
+ return new Float32Array([0.1, 0.2, 0.3, 0.4]);
746
+ };
747
+ store.setEmbeddingFunction(mockEmbedFn);
748
+
749
+ await caret.seal({
750
+ content: 'auto embed test',
751
+ source: 'test://autoembed' as any,
752
+ attribution: 'system',
753
+ });
754
+
755
+ expect(callCount).toBe(1);
756
+ expect(store.embeddingStats().count).toBe(1);
757
+ });
758
+
759
+ test('clearEmbeddings removes all embeddings', async () => {
760
+ const fragment = await caret.seal({
761
+ content: 'clear test',
762
+ source: 'test://clear' as any,
763
+ attribution: 'system',
764
+ });
765
+
766
+ store.setEmbedding(fragment.id, new Float32Array([0.1, 0.2, 0.3, 0.4]));
767
+ expect(store.embeddingStats().count).toBe(1);
768
+
769
+ store.clearEmbeddings();
770
+ expect(store.embeddingStats().count).toBe(0);
771
+ });
772
+ });
773
+
774
+ // ============================================================================
775
+ // INTEGRATION TESTS
776
+ // ============================================================================
777
+
778
+ describe('Integration', () => {
779
+ test('full workflow: seal, resolve, assign credit', async () => {
780
+ const caret = createCaret({ seal_key: SEAL_KEY });
781
+
782
+ // 1. Seal a fragment
783
+ const fragment = await caret.seal({
784
+ content: { task: 'complete integration test' },
785
+ source: 'test://integration' as any,
786
+ attribution: 'system',
787
+ constraints: [
788
+ requireTrust(50),
789
+ requireMCCA(),
790
+ ],
791
+ });
792
+
793
+ // 2. Resolve through constraints
794
+ const resolution = await caret.resolve(fragment, {
795
+ current_time: new Date().toISOString() as Timestamp,
796
+ current_domain: 'test',
797
+ current_trust: 100,
798
+ current_influence: createDefaultInfluenceBudget(),
799
+ });
800
+
801
+ expect(resolution.success).toBe(true);
802
+
803
+ // 3. Create execution trace
804
+ const trace = createTestTrace(4);
805
+
806
+ // 4. Assign credit
807
+ const credits = assignCredit(trace, 1.0);
808
+ expect(validateCreditAssignment(credits)).toBe(true);
809
+
810
+ // 5. Attribute liability
811
+ const liability = attributeLiability(trace);
812
+ expect(validateLiabilityShares(liability)).toBe(true);
813
+ });
814
+
815
+ test('three-layer index workflow', async () => {
816
+ const store = new ExtendedMemoryStore(SEAL_KEY);
817
+ const caret = createCaret({ seal_key: SEAL_KEY, store });
818
+
819
+ // Create documents
820
+ const doc1 = await caret.seal({
821
+ content: 'Machine learning models for NLP',
822
+ source: 'test://ml1' as any,
823
+ attribution: 'system',
824
+ });
825
+
826
+ const doc2 = await caret.seal({
827
+ content: 'Deep learning neural networks',
828
+ source: 'test://ml2' as any,
829
+ attribution: 'system',
830
+ });
831
+
832
+ const doc3 = await caret.seal({
833
+ content: 'Cooking pasta recipes',
834
+ source: 'test://cook' as any,
835
+ attribution: 'system',
836
+ });
837
+
838
+ // Set embeddings
839
+ store.setEmbedding(doc1.id, new Float32Array([0.9, 0.8, 0.1, 0.0]));
840
+ store.setEmbedding(doc2.id, new Float32Array([0.85, 0.75, 0.15, 0.05]));
841
+ store.setEmbedding(doc3.id, new Float32Array([0.1, 0.1, 0.9, 0.8]));
842
+
843
+ // Layer 2: Keyword search
844
+ const keywordResults = await store.queryByKeywords(['learning']);
845
+ expect(keywordResults.length).toBe(2); // doc1 and doc2
846
+
847
+ // Layer 1: Semantic search
848
+ const queryEmb = new Float32Array([0.9, 0.85, 0.1, 0.0]); // ML-like query
849
+ const semanticResults = await store.queryBySimilarity(queryEmb, 0.8, 5);
850
+ expect(semanticResults.length).toBe(2); // doc1 and doc2
851
+ expect(semanticResults[0]!.similarity).toBeGreaterThan(0.9);
852
+
853
+ // Layer 3: Time-based (symbolic)
854
+ const now = new Date();
855
+ const from = new Date(now.getTime() - 60000).toISOString() as Timestamp;
856
+ const to = new Date(now.getTime() + 60000).toISOString() as Timestamp;
857
+ const timeResults = await store.queryByTime(from, to);
858
+ expect(timeResults.length).toBe(3); // All docs
859
+ });
860
+ });