@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,207 @@
1
+ /**
2
+ * Caret — Atomic Encoding Utilities
3
+ * @module @longarcstudios/caret/utils/atomic
4
+ *
5
+ * Coreference resolution for atomic attestation encoding.
6
+ * Transforms ambiguous text into explicit references.
7
+ *
8
+ * Rules:
9
+ * - Pronouns → Agent IDs
10
+ * - Relative time → ISO 8601 timestamps
11
+ * - "it/this/that" → Explicit entity IDs
12
+ * - Ambiguous verbs → MdashActionCode
13
+ */
14
+
15
+ import type { CoreferenceContext, MdashActionCode } from '../types.js';
16
+
17
+ // ============================================================================
18
+ // COREFERENCE PATTERNS
19
+ // ============================================================================
20
+
21
+ interface CoreferencePattern {
22
+ pattern: RegExp;
23
+ resolver: (match: string, context: CoreferenceContext) => string;
24
+ }
25
+
26
+ const COREFERENCE_PATTERNS: CoreferencePattern[] = [
27
+ // Pronouns → Agent IDs
28
+ {
29
+ pattern: /\b(he|she|it|they)\b/gi,
30
+ resolver: (_, ctx) => ctx.lastAgentId ?? 'Agent_unknown',
31
+ },
32
+ // Relative time → ISO 8601
33
+ {
34
+ pattern: /\byesterday\b/gi,
35
+ resolver: (_, ctx) => {
36
+ const d = new Date(ctx.referenceTime);
37
+ d.setDate(d.getDate() - 1);
38
+ return d.toISOString();
39
+ },
40
+ },
41
+ {
42
+ pattern: /\btomorrow\b/gi,
43
+ resolver: (_, ctx) => {
44
+ const d = new Date(ctx.referenceTime);
45
+ d.setDate(d.getDate() + 1);
46
+ return d.toISOString();
47
+ },
48
+ },
49
+ {
50
+ pattern: /\bnow\b/gi,
51
+ resolver: (_, ctx) => new Date(ctx.referenceTime).toISOString(),
52
+ },
53
+ {
54
+ pattern: /\btoday\b/gi,
55
+ resolver: (_, ctx) => {
56
+ const d = new Date(ctx.referenceTime);
57
+ d.setHours(0, 0, 0, 0);
58
+ return d.toISOString();
59
+ },
60
+ },
61
+ // Implicit references → Explicit IDs
62
+ {
63
+ pattern: /\bthe warrant\b/gi,
64
+ resolver: (_, ctx) => ctx.lastWarrantId ?? 'Warrant_unknown',
65
+ },
66
+ {
67
+ pattern: /\bthe checkpoint\b/gi,
68
+ resolver: (_, ctx) => ctx.lastCheckpointId ?? 'Checkpoint_unknown',
69
+ },
70
+ {
71
+ pattern: /\bthe agent\b/gi,
72
+ resolver: (_, ctx) => ctx.lastAgentId ?? 'Agent_unknown',
73
+ },
74
+ ];
75
+
76
+ // ============================================================================
77
+ // MAIN FUNCTIONS
78
+ // ============================================================================
79
+
80
+ /**
81
+ * Resolve coreferences in a text string.
82
+ * Transforms ambiguous language into atomic references.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const result = resolveCoreferences(
87
+ * "He approved it yesterday",
88
+ * {
89
+ * referenceTime: '2026-01-18T12:00:00Z',
90
+ * lastAgentId: 'Agent_0x7f3a',
91
+ * lastWarrantId: 'Warrant_0x8b2c'
92
+ * }
93
+ * );
94
+ * // "Agent_0x7f3a approved Warrant_0x8b2c 2026-01-17T12:00:00.000Z"
95
+ * ```
96
+ */
97
+ export function resolveCoreferences(
98
+ text: string,
99
+ context: CoreferenceContext
100
+ ): string {
101
+ let resolved = text;
102
+
103
+ for (const { pattern, resolver } of COREFERENCE_PATTERNS) {
104
+ resolved = resolved.replace(pattern, (match) => resolver(match, context));
105
+ }
106
+
107
+ return resolved;
108
+ }
109
+
110
+ /**
111
+ * Create a default coreference context with current time.
112
+ */
113
+ export function createDefaultContext(
114
+ overrides?: Partial<CoreferenceContext>
115
+ ): CoreferenceContext {
116
+ return {
117
+ referenceTime: new Date().toISOString(),
118
+ ...overrides,
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Check if text contains unresolved coreferences.
124
+ * Useful for validation before sealing.
125
+ */
126
+ export function hasUnresolvedCoreferences(text: string): boolean {
127
+ const patterns = [
128
+ /\b(he|she|it|they)\b/i,
129
+ /\b(yesterday|tomorrow|now|today)\b/i,
130
+ /\bthe (warrant|checkpoint|agent)\b/i,
131
+ ];
132
+
133
+ return patterns.some(p => p.test(text));
134
+ }
135
+
136
+ /**
137
+ * Extract entity IDs from resolved text.
138
+ * Returns all Agent_, Warrant_, Checkpoint_, User_ prefixed IDs.
139
+ */
140
+ export function extractEntityIds(text: string): string[] {
141
+ const pattern = /\b(Agent|Warrant|Checkpoint|User)_[a-f0-9]+\b/gi;
142
+ const matches = text.match(pattern);
143
+ return matches ? [...new Set(matches)] : [];
144
+ }
145
+
146
+ // ============================================================================
147
+ // CANONICAL ENCODING
148
+ // ============================================================================
149
+
150
+ /**
151
+ * Atomic attestation structure (pre-hash).
152
+ */
153
+ export interface AtomicAttestationInput {
154
+ subject: string;
155
+ action: MdashActionCode;
156
+ object?: string;
157
+ timestamp: string;
158
+ context?: string;
159
+ }
160
+
161
+ /**
162
+ * Create canonical encoding for commitment hash.
163
+ * Deterministic string representation suitable for hashing.
164
+ */
165
+ export function canonicalEncode(attestation: AtomicAttestationInput): string {
166
+ const parts = [
167
+ attestation.subject,
168
+ attestation.action,
169
+ attestation.object ?? '',
170
+ attestation.timestamp,
171
+ attestation.context ?? '',
172
+ ];
173
+
174
+ return parts.join('|');
175
+ }
176
+
177
+ /**
178
+ * Compute SHA-256 hash of canonical encoding.
179
+ * Uses Web Crypto API for cross-platform compatibility.
180
+ */
181
+ export async function computeCommitmentHash(
182
+ attestation: AtomicAttestationInput
183
+ ): Promise<string> {
184
+ const canonical = canonicalEncode(attestation);
185
+ const encoder = new TextEncoder();
186
+ const data = encoder.encode(canonical);
187
+
188
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
189
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
190
+
191
+ return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
192
+ }
193
+
194
+ /**
195
+ * Validate atomic attestation subject format.
196
+ * Must be Entity_hexid pattern.
197
+ */
198
+ export function isValidSubject(subject: string): boolean {
199
+ return /^(Agent|Warrant|Checkpoint|User)_[a-f0-9]+$/i.test(subject);
200
+ }
201
+
202
+ /**
203
+ * Validate ISO 8601 timestamp format.
204
+ */
205
+ export function isValidTimestamp(timestamp: string): boolean {
206
+ return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/.test(timestamp);
207
+ }
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Caret — Credit Assignment Utilities
3
+ * @module @longarcstudios/caret/utils/credit
4
+ *
5
+ * Step-wise credit assignment for execution traces.
6
+ * Distributes terminal reward proportionally to influence consumption.
7
+ *
8
+ * Properties:
9
+ * - Credits sum to terminal reward (conservation)
10
+ * - Higher influence steps receive higher credit
11
+ * - Works for arbitrarily long chains
12
+ */
13
+
14
+ import type {
15
+ ExecutionTrace,
16
+ CreditAssignment,
17
+ StepCredit,
18
+ LiabilityShare,
19
+ InfluenceBudget,
20
+ Timestamp,
21
+ } from '../types.js';
22
+ import { sumInfluence } from '../types.js';
23
+
24
+ // ============================================================================
25
+ // CREDIT ASSIGNMENT
26
+ // ============================================================================
27
+
28
+ /**
29
+ * Assign credit to execution steps proportional to influence consumption.
30
+ * Terminal reward is distributed to all steps based on their contribution.
31
+ *
32
+ * @param trace - Execution trace with steps
33
+ * @param terminalReward - Final outcome reward to distribute
34
+ * @returns Credit assignment for all steps
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const credits = assignCredit(trace, 1.0);
39
+ * // Sum of credits.step_credits[].credit === 1.0
40
+ * ```
41
+ */
42
+ export function assignCredit(
43
+ trace: ExecutionTrace,
44
+ terminalReward: number
45
+ ): CreditAssignment {
46
+ // Compute total influence
47
+ const totalInfluence = trace.steps.reduce(
48
+ (sum, step) => sum + sumInfluence(step.influence_consumed),
49
+ 0
50
+ );
51
+
52
+ // Distribute credit proportionally
53
+ const stepCredits: StepCredit[] = trace.steps.map((step) => {
54
+ const influence = sumInfluence(step.influence_consumed);
55
+ const influenceFraction = totalInfluence > 0 ? influence / totalInfluence : 0;
56
+
57
+ return {
58
+ step_id: step.step_id,
59
+ credit: influenceFraction * terminalReward,
60
+ influence,
61
+ influence_fraction: influenceFraction,
62
+ };
63
+ });
64
+
65
+ return {
66
+ trace_id: trace.trace_id,
67
+ terminal_reward: terminalReward,
68
+ step_credits: stepCredits,
69
+ assigned_at: new Date().toISOString() as Timestamp,
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Validate that credit assignment conserves terminal reward.
75
+ * Credits should sum to terminal reward within tolerance.
76
+ */
77
+ export function validateCreditAssignment(
78
+ assignment: CreditAssignment,
79
+ tolerance: number = 0.001
80
+ ): boolean {
81
+ const creditSum = assignment.step_credits.reduce(
82
+ (acc, step) => acc + step.credit,
83
+ 0
84
+ );
85
+ return Math.abs(creditSum - assignment.terminal_reward) < tolerance;
86
+ }
87
+
88
+ // ============================================================================
89
+ // LIABILITY ATTRIBUTION
90
+ // ============================================================================
91
+
92
+ /**
93
+ * Extract agent ID from step ID.
94
+ * Convention: step IDs prefixed with agent identifier.
95
+ */
96
+ function extractAgentId(stepId: string): string {
97
+ const match = stepId.match(/^(Agent_[a-f0-9]+)/i);
98
+ return match && match[1] ? match[1] : 'Agent_unknown';
99
+ }
100
+
101
+ /**
102
+ * Attribute liability to contributing agents based on influence consumption.
103
+ *
104
+ * @param trace - Execution trace
105
+ * @returns Liability shares summing to 1.0
106
+ */
107
+ export function attributeLiability(trace: ExecutionTrace): LiabilityShare[] {
108
+ // Compute total influence
109
+ const totalInfluence = trace.steps.reduce(
110
+ (sum, step) => sum + sumInfluence(step.influence_consumed),
111
+ 0
112
+ );
113
+
114
+ if (totalInfluence === 0) {
115
+ return []; // No liability if no influence consumed
116
+ }
117
+
118
+ // Group steps by agent
119
+ const agentContributions = new Map<string, {
120
+ influence: number;
121
+ steps: string[];
122
+ }>();
123
+
124
+ for (const step of trace.steps) {
125
+ const agentId = extractAgentId(step.step_id);
126
+ const stepInfluence = sumInfluence(step.influence_consumed);
127
+
128
+ const existing = agentContributions.get(agentId) ?? { influence: 0, steps: [] };
129
+ existing.influence += stepInfluence;
130
+ existing.steps.push(step.step_id);
131
+ agentContributions.set(agentId, existing);
132
+ }
133
+
134
+ // Convert to liability shares
135
+ return Array.from(agentContributions.entries()).map(([agentId, data]) => ({
136
+ agent_id: agentId,
137
+ contribution: data.influence / totalInfluence,
138
+ evidence: data.steps,
139
+ }));
140
+ }
141
+
142
+ /**
143
+ * Validate that liability shares sum to 1.0 within tolerance.
144
+ */
145
+ export function validateLiabilityShares(
146
+ shares: LiabilityShare[],
147
+ tolerance: number = 0.001
148
+ ): boolean {
149
+ if (shares.length === 0) return true; // Empty is valid
150
+
151
+ const sum = shares.reduce((acc, share) => acc + share.contribution, 0);
152
+ return Math.abs(sum - 1.0) < tolerance;
153
+ }
154
+
155
+ // ============================================================================
156
+ // INFLUENCE UTILITIES
157
+ // ============================================================================
158
+
159
+ /**
160
+ * Create MCCA-compliant default influence budget.
161
+ */
162
+ export function createDefaultInfluenceBudget(): InfluenceBudget {
163
+ return {
164
+ system: 0.45,
165
+ user: 0.30,
166
+ environment: 0.15,
167
+ assistant: 0.10,
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Check if influence budget is MCCA-compliant.
173
+ */
174
+ export function isMCCACompliant(budget: InfluenceBudget): boolean {
175
+ return (
176
+ budget.system >= 0.40 &&
177
+ budget.user <= 0.35 &&
178
+ budget.environment <= 0.20 &&
179
+ budget.assistant <= 0.25
180
+ );
181
+ }
182
+
183
+ /**
184
+ * Normalize influence budget to sum to 1.0.
185
+ */
186
+ export function normalizeInfluenceBudget(budget: InfluenceBudget): InfluenceBudget {
187
+ const total = sumInfluence(budget);
188
+
189
+ if (total === 0) {
190
+ return createDefaultInfluenceBudget();
191
+ }
192
+
193
+ return {
194
+ system: budget.system / total,
195
+ user: budget.user / total,
196
+ environment: budget.environment / total,
197
+ assistant: budget.assistant / total,
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Add two influence budgets.
203
+ */
204
+ export function addInfluenceBudgets(
205
+ a: InfluenceBudget,
206
+ b: InfluenceBudget
207
+ ): InfluenceBudget {
208
+ return {
209
+ system: a.system + b.system,
210
+ user: a.user + b.user,
211
+ environment: a.environment + b.environment,
212
+ assistant: a.assistant + b.assistant,
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Compute total influence consumed in a trace.
218
+ */
219
+ export function computeTraceInfluence(trace: ExecutionTrace): InfluenceBudget {
220
+ return trace.steps.reduce(
221
+ (total, step) => addInfluenceBudgets(total, step.influence_consumed),
222
+ { system: 0, user: 0, environment: 0, assistant: 0 }
223
+ );
224
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Caret — Utility Functions
3
+ * @module @longarcstudios/caret/utils
4
+ *
5
+ * Research pattern utilities for Caret v0.2.0:
6
+ * - Atomic encoding (coreference resolution)
7
+ * - Utility learning (incremental score updates)
8
+ * - Credit assignment (step-wise reward distribution)
9
+ */
10
+
11
+ export * from './atomic.js';
12
+ export * from './utility.js';
13
+ export * from './credit.js';
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Caret — Utility Learning Functions
3
+ * @module @longarcstudios/caret/utils/utility
4
+ *
5
+ * Incremental utility score learning.
6
+ * Implements: score ← score + α(reward - score)
7
+ */
8
+
9
+ import type {
10
+ Outcome,
11
+ UtilityScore,
12
+ UtilityFeedback,
13
+ } from '../types.js';
14
+
15
+ // ============================================================================
16
+ // CONSTANTS
17
+ // ============================================================================
18
+
19
+ /** Default learning rate for utility updates */
20
+ const DEFAULT_ALPHA = 0.1;
21
+
22
+ /** Confidence saturation constant (updates needed for ~90% confidence) */
23
+ const CONFIDENCE_TAU = 10;
24
+
25
+ // ============================================================================
26
+ // UTILITY UPDATE
27
+ // ============================================================================
28
+
29
+ /**
30
+ * Update utility score based on feedback.
31
+ * Implements incremental learning: score ← score + α(reward - score)
32
+ *
33
+ * @param current - Current utility score
34
+ * @param feedback - Feedback to incorporate
35
+ * @param alpha - Learning rate (default 0.1)
36
+ * @returns Updated utility score (immutable)
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const updated = updateUtilityScore(
41
+ * { score: 0.5, confidence: 0.3, update_count: 3 },
42
+ * { reward: 0.8, source: 'human', timestamp: '2026-01-18T12:00:00Z' }
43
+ * );
44
+ * // { score: 0.53, confidence: 0.33, update_count: 4 }
45
+ * ```
46
+ */
47
+ export function updateUtilityScore(
48
+ current: UtilityScore,
49
+ feedback: UtilityFeedback,
50
+ alpha: number = DEFAULT_ALPHA
51
+ ): UtilityScore {
52
+ // Clamp feedback reward to [-1, 1]
53
+ const reward = Math.max(-1, Math.min(1, feedback.reward));
54
+
55
+ // Incremental update
56
+ const newScore = Math.max(-1, Math.min(1,
57
+ current.score + alpha * (reward - current.score)
58
+ ));
59
+
60
+ // Update confidence based on count
61
+ const newCount = current.update_count + 1;
62
+ const newConfidence = Math.min(0.99, 1 - Math.exp(-newCount / CONFIDENCE_TAU));
63
+
64
+ return {
65
+ score: newScore,
66
+ confidence: newConfidence,
67
+ update_count: newCount,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Update an Outcome's utility score based on feedback.
73
+ * Returns a new Outcome with updated utility (immutable).
74
+ */
75
+ export function updateOutcomeUtility<T>(
76
+ outcome: Outcome<T>,
77
+ feedback: UtilityFeedback,
78
+ alpha: number = DEFAULT_ALPHA
79
+ ): Outcome<T> {
80
+ return {
81
+ ...outcome,
82
+ utility: updateUtilityScore(outcome.utility, feedback, alpha),
83
+ };
84
+ }
85
+
86
+ // ============================================================================
87
+ // UTILITY INITIALIZATION
88
+ // ============================================================================
89
+
90
+ /**
91
+ * Create a fresh utility score (no observations yet).
92
+ */
93
+ export function createInitialUtility(priorScore: number = 0): UtilityScore {
94
+ return {
95
+ score: Math.max(-1, Math.min(1, priorScore)),
96
+ confidence: 0,
97
+ update_count: 0,
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Create utility score from historical observations.
103
+ */
104
+ export function createUtilityFromHistory(
105
+ rewards: number[],
106
+ alpha: number = DEFAULT_ALPHA
107
+ ): UtilityScore {
108
+ let utility = createInitialUtility();
109
+
110
+ for (const reward of rewards) {
111
+ utility = updateUtilityScore(
112
+ utility,
113
+ { reward, source: 'system', timestamp: new Date().toISOString() as any },
114
+ alpha
115
+ );
116
+ }
117
+
118
+ return utility;
119
+ }
120
+
121
+ // ============================================================================
122
+ // UTILITY PREDICTION
123
+ // ============================================================================
124
+
125
+ /**
126
+ * Predict success probability from historical outcomes.
127
+ * Uses exponential moving average with recency bias.
128
+ *
129
+ * @param historicalScores - Array of past utility scores
130
+ * @param decayFactor - Weight decay per position (default 0.9)
131
+ * @returns Predicted success probability [0, 1]
132
+ */
133
+ export function predictSuccess(
134
+ historicalScores: UtilityScore[],
135
+ decayFactor: number = 0.9
136
+ ): number {
137
+ if (historicalScores.length === 0) {
138
+ return 0.5; // Uninformative prior
139
+ }
140
+
141
+ // Sort by update count (proxy for recency if no timestamps)
142
+ const sorted = [...historicalScores].sort(
143
+ (a, b) => b.update_count - a.update_count
144
+ );
145
+
146
+ let weightedSum = 0;
147
+ let weightSum = 0;
148
+
149
+ for (let i = 0; i < sorted.length; i++) {
150
+ const score = sorted[i];
151
+ if (!score) continue;
152
+
153
+ const weight = Math.pow(decayFactor, i);
154
+ // Map score from [-1, 1] to [0, 1] for probability
155
+ const prob = (score.score + 1) / 2;
156
+
157
+ weightedSum += weight * prob;
158
+ weightSum += weight;
159
+ }
160
+
161
+ return weightedSum / weightSum;
162
+ }
163
+
164
+ /**
165
+ * Compute expected utility given success probability and reward magnitudes.
166
+ */
167
+ export function expectedUtility(
168
+ successProb: number,
169
+ successReward: number,
170
+ failureReward: number
171
+ ): number {
172
+ return successProb * successReward + (1 - successProb) * failureReward;
173
+ }
174
+
175
+ // ============================================================================
176
+ // UTILITY COMPARISON
177
+ // ============================================================================
178
+
179
+ /**
180
+ * Compare two utility scores, accounting for confidence.
181
+ * Returns positive if a > b, negative if a < b, zero if equivalent.
182
+ */
183
+ export function compareUtility(a: UtilityScore, b: UtilityScore): number {
184
+ // Weight by confidence
185
+ const aWeighted = a.score * a.confidence;
186
+ const bWeighted = b.score * b.confidence;
187
+
188
+ return aWeighted - bWeighted;
189
+ }
190
+
191
+ /**
192
+ * Select the best outcome from a list by utility.
193
+ */
194
+ export function selectBestOutcome<T>(outcomes: Outcome<T>[]): Outcome<T> | null {
195
+ if (outcomes.length === 0) return null;
196
+
197
+ return outcomes.reduce((best, current) =>
198
+ compareUtility(current.utility, best.utility) > 0 ? current : best
199
+ );
200
+ }