martin-loop 0.1.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/CODE_OF_CONDUCT.md +32 -0
  2. package/LICENSE +21 -21
  3. package/README.md +307 -398
  4. package/demo/seeded-workspace/README.md +35 -35
  5. package/demo/seeded-workspace/TASKS.md +29 -29
  6. package/demo/seeded-workspace/martin.config.yaml +11 -11
  7. package/demo/seeded-workspace/package.json +8 -8
  8. package/demo/seeded-workspace/src/invoice-summary.js +11 -11
  9. package/demo/seeded-workspace/test/invoice-summary.test.js +20 -20
  10. package/dist/bin/martin-loop.js +0 -0
  11. package/dist/vendor/adapters/counter.d.ts +1 -0
  12. package/dist/vendor/adapters/counter.js +4 -0
  13. package/dist/vendor/adapters/git-baseline.d.ts +50 -0
  14. package/dist/vendor/adapters/git-baseline.js +233 -0
  15. package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
  16. package/dist/vendor/adapters/openrouter-adapter.js +302 -0
  17. package/dist/vendor/adapters/usage.d.ts +48 -0
  18. package/dist/vendor/adapters/usage.js +66 -0
  19. package/dist/vendor/cli/bin/exit.d.ts +12 -0
  20. package/dist/vendor/cli/bin/exit.js +28 -0
  21. package/dist/vendor/cli/commands/analyze.d.ts +5 -0
  22. package/dist/vendor/cli/commands/analyze.js +58 -0
  23. package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
  24. package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
  25. package/dist/vendor/cli/commands/audit.d.ts +8 -0
  26. package/dist/vendor/cli/commands/audit.js +199 -0
  27. package/dist/vendor/cli/commands/corpus.d.ts +5 -0
  28. package/dist/vendor/cli/commands/corpus.js +60 -0
  29. package/dist/vendor/cli/commands/doctor.d.ts +8 -0
  30. package/dist/vendor/cli/commands/doctor.js +219 -0
  31. package/dist/vendor/cli/commands/explain.d.ts +17 -0
  32. package/dist/vendor/cli/commands/explain.js +176 -0
  33. package/dist/vendor/cli/commands/export.d.ts +5 -0
  34. package/dist/vendor/cli/commands/export.js +60 -0
  35. package/dist/vendor/cli/commands/governance.d.ts +8 -0
  36. package/dist/vendor/cli/commands/governance.js +95 -0
  37. package/dist/vendor/cli/commands/improve.d.ts +18 -0
  38. package/dist/vendor/cli/commands/improve.js +396 -0
  39. package/dist/vendor/cli/commands/init.d.ts +8 -0
  40. package/dist/vendor/cli/commands/init.js +281 -0
  41. package/dist/vendor/cli/commands/migration.d.ts +8 -0
  42. package/dist/vendor/cli/commands/migration.js +67 -0
  43. package/dist/vendor/cli/commands/prior.d.ts +23 -0
  44. package/dist/vendor/cli/commands/prior.js +145 -0
  45. package/dist/vendor/cli/commands/resume.d.ts +21 -0
  46. package/dist/vendor/cli/commands/resume.js +73 -0
  47. package/dist/vendor/cli/commands/verify.d.ts +6 -0
  48. package/dist/vendor/cli/commands/verify.js +43 -0
  49. package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
  50. package/dist/vendor/cli/research/public-corpus.js +151 -0
  51. package/dist/vendor/cli/ui/error-card.d.ts +38 -0
  52. package/dist/vendor/cli/ui/error-card.js +103 -0
  53. package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
  54. package/dist/vendor/cli/ui/mission-brief.js +173 -0
  55. package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
  56. package/dist/vendor/cli/ui/summary-card.js +102 -0
  57. package/dist/vendor/contracts/audit.d.ts +46 -0
  58. package/dist/vendor/contracts/audit.js +360 -0
  59. package/dist/vendor/contracts/post-phase15.d.ts +240 -0
  60. package/dist/vendor/contracts/post-phase15.js +166 -0
  61. package/dist/vendor/core/agent/mandates.d.ts +46 -0
  62. package/dist/vendor/core/agent/mandates.js +178 -0
  63. package/dist/vendor/core/agent/receipts.d.ts +38 -0
  64. package/dist/vendor/core/agent/receipts.js +131 -0
  65. package/dist/vendor/core/agent/signing.d.ts +17 -0
  66. package/dist/vendor/core/agent/signing.js +91 -0
  67. package/dist/vendor/core/attestation/sign.d.ts +25 -0
  68. package/dist/vendor/core/attestation/sign.js +216 -0
  69. package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
  70. package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
  71. package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
  72. package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
  73. package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
  74. package/dist/vendor/core/autonomy/envelope.js +27 -0
  75. package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
  76. package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
  77. package/dist/vendor/core/autonomy/resume.d.ts +15 -0
  78. package/dist/vendor/core/autonomy/resume.js +23 -0
  79. package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
  80. package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
  81. package/dist/vendor/core/context-distillation.d.ts +3 -0
  82. package/dist/vendor/core/context-distillation.js +44 -0
  83. package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
  84. package/dist/vendor/core/context-flow/compile-context.js +111 -0
  85. package/dist/vendor/core/context-flow/entities.d.ts +2 -0
  86. package/dist/vendor/core/context-flow/entities.js +44 -0
  87. package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
  88. package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
  89. package/dist/vendor/core/context-flow/index.d.ts +11 -0
  90. package/dist/vendor/core/context-flow/index.js +24 -0
  91. package/dist/vendor/core/context-flow/labels.d.ts +3 -0
  92. package/dist/vendor/core/context-flow/labels.js +17 -0
  93. package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
  94. package/dist/vendor/core/context-flow/normalizer.js +69 -0
  95. package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
  96. package/dist/vendor/core/context-flow/profiles.js +36 -0
  97. package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
  98. package/dist/vendor/core/context-flow/redaction.js +6 -0
  99. package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
  100. package/dist/vendor/core/context-flow/sensitivity.js +27 -0
  101. package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
  102. package/dist/vendor/core/context-flow/sync-preview.js +22 -0
  103. package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
  104. package/dist/vendor/core/context-flow/token-estimator.js +13 -0
  105. package/dist/vendor/core/context-flow/types.d.ts +91 -0
  106. package/dist/vendor/core/context-flow/types.js +2 -0
  107. package/dist/vendor/core/context-utility.d.ts +47 -0
  108. package/dist/vendor/core/context-utility.js +405 -0
  109. package/dist/vendor/core/cost/pipeline.d.ts +92 -0
  110. package/dist/vendor/core/cost/pipeline.js +141 -0
  111. package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
  112. package/dist/vendor/core/cost/tagged-cost.js +55 -0
  113. package/dist/vendor/core/cost-governor.d.ts +2 -0
  114. package/dist/vendor/core/cost-governor.js +50 -0
  115. package/dist/vendor/core/cve/cve-check.d.ts +80 -0
  116. package/dist/vendor/core/cve/cve-check.js +172 -0
  117. package/dist/vendor/core/digital-twin/index.d.ts +27 -0
  118. package/dist/vendor/core/digital-twin/index.js +90 -0
  119. package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
  120. package/dist/vendor/core/drift/drift-graph.js +100 -0
  121. package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
  122. package/dist/vendor/core/drift/objective-lock.js +88 -0
  123. package/dist/vendor/core/drift/scope.d.ts +46 -0
  124. package/dist/vendor/core/drift/scope.js +102 -0
  125. package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
  126. package/dist/vendor/core/drift/signature-lock.js +202 -0
  127. package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
  128. package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
  129. package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
  130. package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
  131. package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
  132. package/dist/vendor/core/evidence/claim-audit.js +89 -0
  133. package/dist/vendor/core/exit-intelligence.d.ts +2 -0
  134. package/dist/vendor/core/exit-intelligence.js +58 -0
  135. package/dist/vendor/core/explain/formatter.d.ts +42 -0
  136. package/dist/vendor/core/explain/formatter.js +171 -0
  137. package/dist/vendor/core/explain/timeline.d.ts +29 -0
  138. package/dist/vendor/core/explain/timeline.js +213 -0
  139. package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
  140. package/dist/vendor/core/failure-taxonomy.js +76 -0
  141. package/dist/vendor/core/gateway/index.d.ts +10 -0
  142. package/dist/vendor/core/gateway/index.js +12 -0
  143. package/dist/vendor/core/gateway/registry.d.ts +40 -0
  144. package/dist/vendor/core/gateway/registry.js +97 -0
  145. package/dist/vendor/core/gateway/transport.d.ts +31 -0
  146. package/dist/vendor/core/gateway/transport.js +82 -0
  147. package/dist/vendor/core/gateway/vault.d.ts +19 -0
  148. package/dist/vendor/core/gateway/vault.js +29 -0
  149. package/dist/vendor/core/graph/adapters.d.ts +43 -0
  150. package/dist/vendor/core/graph/adapters.js +91 -0
  151. package/dist/vendor/core/graph/hotspots.d.ts +22 -0
  152. package/dist/vendor/core/graph/hotspots.js +30 -0
  153. package/dist/vendor/core/graph/index.d.ts +1 -0
  154. package/dist/vendor/core/graph/index.js +2 -0
  155. package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
  156. package/dist/vendor/core/honey/honey-tokens.js +44 -0
  157. package/dist/vendor/core/index.d.ts +2 -2
  158. package/dist/vendor/core/index.js +38 -12
  159. package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
  160. package/dist/vendor/core/learning/bayesian-update.js +60 -0
  161. package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
  162. package/dist/vendor/core/learning/prior-sets.js +111 -0
  163. package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
  164. package/dist/vendor/core/learning/promotion-gate.js +23 -0
  165. package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
  166. package/dist/vendor/core/leash/blast-radius.js +156 -0
  167. package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
  168. package/dist/vendor/core/leash/policy-leash.js +117 -0
  169. package/dist/vendor/core/memo/memo.d.ts +63 -0
  170. package/dist/vendor/core/memo/memo.js +97 -0
  171. package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
  172. package/dist/vendor/core/memory/learning-pipeline.js +391 -0
  173. package/dist/vendor/core/memory/palace.d.ts +84 -0
  174. package/dist/vendor/core/memory/palace.js +379 -0
  175. package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
  176. package/dist/vendor/core/merge/ast-merge.js +350 -0
  177. package/dist/vendor/core/merge/text-merge.d.ts +12 -0
  178. package/dist/vendor/core/merge/text-merge.js +182 -0
  179. package/dist/vendor/core/otel/tracer.d.ts +45 -0
  180. package/dist/vendor/core/otel/tracer.js +116 -0
  181. package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
  182. package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
  183. package/dist/vendor/core/parallel/scorer.d.ts +24 -0
  184. package/dist/vendor/core/parallel/scorer.js +65 -0
  185. package/dist/vendor/core/pattern-detection.d.ts +64 -0
  186. package/dist/vendor/core/pattern-detection.js +108 -0
  187. package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
  188. package/dist/vendor/core/persistence/checkpoint.js +156 -0
  189. package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
  190. package/dist/vendor/core/persistence/cleanup.js +131 -0
  191. package/dist/vendor/core/persistence/index.d.ts +2 -0
  192. package/dist/vendor/core/persistence/index.js +1 -0
  193. package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
  194. package/dist/vendor/core/persistence/runs-reader.js +84 -0
  195. package/dist/vendor/core/persistence/store.d.ts +6 -1
  196. package/dist/vendor/core/persistence/store.js +5 -0
  197. package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
  198. package/dist/vendor/core/policy/file-touch-quota.js +105 -0
  199. package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
  200. package/dist/vendor/core/policy/policy-loader.js +170 -0
  201. package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
  202. package/dist/vendor/core/policy/policy-schema.js +78 -0
  203. package/dist/vendor/core/probe/probe.d.ts +49 -0
  204. package/dist/vendor/core/probe/probe.js +115 -0
  205. package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
  206. package/dist/vendor/core/proof/patch-proof.js +84 -0
  207. package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
  208. package/dist/vendor/core/proof/semantic-probe.js +82 -0
  209. package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
  210. package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
  211. package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
  212. package/dist/vendor/core/red-blue/red-phase.js +141 -0
  213. package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
  214. package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
  215. package/dist/vendor/core/replay/replay.d.ts +85 -0
  216. package/dist/vendor/core/replay/replay.js +109 -0
  217. package/dist/vendor/core/router/engine.d.ts +54 -0
  218. package/dist/vendor/core/router/engine.js +131 -0
  219. package/dist/vendor/core/router/index.d.ts +1 -0
  220. package/dist/vendor/core/router/index.js +2 -0
  221. package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
  222. package/dist/vendor/core/router/trust-calibration.js +127 -0
  223. package/dist/vendor/core/run-martin.d.ts +2 -0
  224. package/dist/vendor/core/run-martin.js +287 -0
  225. package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
  226. package/dist/vendor/core/security/cve-scanner.js +178 -0
  227. package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
  228. package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
  229. package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
  230. package/dist/vendor/core/sentinel/progress-guard.js +46 -0
  231. package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
  232. package/dist/vendor/core/siem/siem-emitter.js +157 -0
  233. package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
  234. package/dist/vendor/core/strategy/attempt-brief.js +89 -0
  235. package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
  236. package/dist/vendor/core/summarize/diff-summary.js +204 -0
  237. package/dist/vendor/core/surface-signals.d.ts +21 -0
  238. package/dist/vendor/core/surface-signals.js +139 -0
  239. package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
  240. package/dist/vendor/core/truth/truth-wall.js +69 -0
  241. package/dist/vendor/core/truth-spine.d.ts +26 -0
  242. package/dist/vendor/core/truth-spine.js +62 -0
  243. package/dist/vendor/core/types.d.ts +115 -0
  244. package/dist/vendor/core/types.js +2 -0
  245. package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
  246. package/dist/vendor/core/verification/tiered-verify.js +29 -0
  247. package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
  248. package/dist/vendor/core/verifier-pyramid.js +111 -0
  249. package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
  250. package/dist/vendor/core/workflow-artifacts.js +668 -0
  251. package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
  252. package/dist/vendor/core/wrap/supervised-run.js +178 -0
  253. package/docs/assets/cli-animated.svg +139 -0
  254. package/docs/assets/cli-static.svg +34 -0
  255. package/docs/assets/github-hero-v2.svg +23 -0
  256. package/docs/assets/martin-raplph.png.jpg +0 -0
  257. package/docs/assets/martinloop-logo.png +0 -0
  258. package/docs/assets/nvidia-inception-program-light.png +0 -0
  259. package/docs/assets/nvidia-inception-program.png +0 -0
  260. package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
  261. package/docs/assets/side-by-side.svg +134 -0
  262. package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -142
  263. package/docs/oss/EXAMPLES.md +134 -134
  264. package/docs/oss/OSS-BOUNDARY-REPORT.json +1 -1
  265. package/docs/oss/OSS-BOUNDARY-REPORT.md +1 -1
  266. package/docs/oss/QUICKSTART.md +170 -165
  267. package/docs/oss/RALPH-LOOP-SAFETY.md +113 -113
  268. package/docs/oss/README.md +96 -96
  269. package/docs/oss/RELEASE-SURFACE-REPORT.json +2 -1
  270. package/docs/oss/RELEASE-SURFACE-REPORT.md +2 -1
  271. package/package.json +130 -58
  272. package/docs/distribution/DIRECTORY-SUBMISSIONS.md +0 -89
  273. package/docs/distribution/INTEGRATION-OUTREACH.md +0 -61
  274. package/docs/distribution/UNDER-3-CHALLENGE.md +0 -65
@@ -0,0 +1,143 @@
1
+ // SLICE-24 — Model circuit breaker
2
+ // State machine: CLOSED → OPEN → HALF_OPEN → CLOSED
3
+ // Tracks consecutive failures per adapter and opens the circuit when the
4
+ // threshold is reached, allowing recovery via a half-open probe.
5
+ const DEFAULT_CONFIG = {
6
+ failureThreshold: 3,
7
+ cooldownMs: 60_000,
8
+ windowMs: 300_000
9
+ };
10
+ export class CircuitBreaker {
11
+ config;
12
+ adapterId;
13
+ state = "CLOSED";
14
+ failureCount = 0;
15
+ lastFailureTime;
16
+ openedAt;
17
+ constructor(adapterId, config) {
18
+ this.adapterId = adapterId;
19
+ this.config = { ...DEFAULT_CONFIG, ...config };
20
+ }
21
+ /**
22
+ * Returns the current circuit breaker state snapshot.
23
+ */
24
+ getState() {
25
+ return {
26
+ state: this.state,
27
+ failureCount: this.failureCount,
28
+ lastFailureTime: this.lastFailureTime?.toISOString(),
29
+ openedAt: this.openedAt?.toISOString(),
30
+ adapterId: this.adapterId
31
+ };
32
+ }
33
+ /**
34
+ * Returns true if a call should be allowed through.
35
+ * - CLOSED: always allowed
36
+ * - HALF_OPEN: one probe call allowed
37
+ * - OPEN: blocked unless cooldown has elapsed (transitions to HALF_OPEN)
38
+ */
39
+ canCall(now = new Date()) {
40
+ if (this.state === "CLOSED") {
41
+ return true;
42
+ }
43
+ if (this.state === "HALF_OPEN") {
44
+ return true;
45
+ }
46
+ // OPEN: check if cooldown has elapsed
47
+ if (this.state === "OPEN" && this.openedAt) {
48
+ const elapsed = now.getTime() - this.openedAt.getTime();
49
+ if (elapsed >= this.config.cooldownMs) {
50
+ this.state = "HALF_OPEN";
51
+ return true;
52
+ }
53
+ }
54
+ return false;
55
+ }
56
+ /**
57
+ * Record a successful adapter call.
58
+ * HALF_OPEN → CLOSED on success.
59
+ */
60
+ recordSuccess() {
61
+ if (this.state === "HALF_OPEN") {
62
+ this.state = "CLOSED";
63
+ this.failureCount = 0;
64
+ this.lastFailureTime = undefined;
65
+ this.openedAt = undefined;
66
+ }
67
+ else if (this.state === "CLOSED") {
68
+ // Reset failure count on success in closed state
69
+ this.failureCount = 0;
70
+ }
71
+ // In OPEN state, success shouldn't happen (canCall returns false)
72
+ // but we handle it gracefully
73
+ }
74
+ /**
75
+ * Record a failed adapter call.
76
+ * CLOSED: increments failure count; if threshold reached, opens circuit.
77
+ * HALF_OPEN: re-opens circuit (probe failed).
78
+ */
79
+ recordFailure(now = new Date()) {
80
+ this.lastFailureTime = now;
81
+ if (this.state === "HALF_OPEN") {
82
+ // Probe failed — re-open
83
+ this.state = "OPEN";
84
+ this.openedAt = now;
85
+ return;
86
+ }
87
+ if (this.state === "CLOSED") {
88
+ this.failureCount++;
89
+ if (this.failureCount >= this.config.failureThreshold) {
90
+ this.state = "OPEN";
91
+ this.openedAt = now;
92
+ }
93
+ }
94
+ // In OPEN state, failures are noted but don't change state
95
+ }
96
+ /**
97
+ * Manually reset the circuit breaker to CLOSED state.
98
+ */
99
+ reset() {
100
+ this.state = "CLOSED";
101
+ this.failureCount = 0;
102
+ this.lastFailureTime = undefined;
103
+ this.openedAt = undefined;
104
+ }
105
+ }
106
+ /**
107
+ * Serializes a CircuitBreaker instance to a JSON string.
108
+ */
109
+ export function serializeState(breaker) {
110
+ const snapshot = breaker.getState();
111
+ const serialized = {
112
+ adapterId: snapshot.adapterId,
113
+ config: breaker.config,
114
+ internalState: {
115
+ state: snapshot.state,
116
+ failureCount: snapshot.failureCount,
117
+ lastFailureTime: snapshot.lastFailureTime,
118
+ openedAt: snapshot.openedAt
119
+ }
120
+ };
121
+ return JSON.stringify(serialized);
122
+ }
123
+ /**
124
+ * Deserializes a JSON string back into a CircuitBreaker instance.
125
+ * Restores internal state including failure count, timestamps, and state.
126
+ */
127
+ export function deserializeState(json, configOverride) {
128
+ const parsed = JSON.parse(json);
129
+ const mergedConfig = { ...parsed.config, ...configOverride };
130
+ const breaker = new CircuitBreaker(parsed.adapterId, mergedConfig);
131
+ // Restore internal state by directly setting private fields via index access
132
+ const internal = parsed.internalState;
133
+ breaker["state"] = internal.state;
134
+ breaker["failureCount"] = internal.failureCount;
135
+ breaker["lastFailureTime"] = internal.lastFailureTime
136
+ ? new Date(internal.lastFailureTime)
137
+ : undefined;
138
+ breaker["openedAt"] = internal.openedAt
139
+ ? new Date(internal.openedAt)
140
+ : undefined;
141
+ return breaker;
142
+ }
143
+ //# sourceMappingURL=circuit-breaker.js.map
@@ -0,0 +1,3 @@
1
+ import type { LoopRecord } from "../contracts/index.js";
2
+ import type { DistillContextOptions, DistilledContext } from "./types.js";
3
+ export declare function distillContext(loop: LoopRecord, options?: DistillContextOptions): DistilledContext;
@@ -0,0 +1,44 @@
1
+ export function distillContext(loop, options = {}) {
2
+ const maxRecentAttempts = Math.max(1, options.maxRecentAttempts ?? 3);
3
+ const recentAttempts = loop.attempts.slice(-maxRecentAttempts).map((attempt) => {
4
+ const distilled = {
5
+ attemptId: attempt.attemptId,
6
+ index: attempt.index,
7
+ summary: attempt.summary ?? "No attempt summary recorded."
8
+ };
9
+ if (attempt.failureClass) {
10
+ distilled.failureClass = attempt.failureClass;
11
+ }
12
+ if (attempt.intervention) {
13
+ distilled.intervention = attempt.intervention;
14
+ }
15
+ return distilled;
16
+ });
17
+ const remainingBudgetUsd = clamp(loop.budget.maxUsd - loop.cost.actualUsd);
18
+ const remainingIterations = clamp(loop.budget.maxIterations - loop.attempts.length);
19
+ const remainingTokens = clamp(loop.budget.maxTokens - loop.cost.tokensIn - loop.cost.tokensOut);
20
+ const repeatedClass = recentAttempts.at(-1)?.failureClass;
21
+ const nextMove = repeatedClass === undefined
22
+ ? "Focus on the first verified attempt."
23
+ : `Avoid another ${repeatedClass} outcome.`;
24
+ const context = {
25
+ taskTitle: loop.task.title,
26
+ objective: loop.task.objective,
27
+ verificationPlan: [...loop.task.verificationPlan],
28
+ recentAttempts,
29
+ constraints: {
30
+ remainingBudgetUsd,
31
+ remainingIterations,
32
+ remainingTokens
33
+ },
34
+ focus: `${loop.task.objective} ${nextMove}`.trim()
35
+ };
36
+ if (loop.task.repoRoot) {
37
+ context.repoRoot = loop.task.repoRoot;
38
+ }
39
+ return context;
40
+ }
41
+ function clamp(value) {
42
+ return value < 0 ? 0 : Number(value.toFixed(4));
43
+ }
44
+ //# sourceMappingURL=context-distillation.js.map
@@ -0,0 +1,8 @@
1
+ import type { CanonicalRecord, CompileOptions, ContextPack, PolicyDecision, QcReport } from "./types.js";
2
+ export declare function defaultCompileOptions(): CompileOptions;
3
+ export declare function compileRecordContext(record: CanonicalRecord, profileName: string | undefined, options?: Partial<CompileOptions>): {
4
+ profile: ContextPack["profile"];
5
+ compiledContext: string;
6
+ qcReport: QcReport;
7
+ };
8
+ export declare function createCompileTraceId(record: CanonicalRecord, compiledContext: string, policyDecision: PolicyDecision): string;
@@ -0,0 +1,111 @@
1
+ import { createHash } from "node:crypto";
2
+ import { resolveHeadlessProfile } from "./profiles.js";
3
+ import { redactText } from "./redaction.js";
4
+ import { estimateTokens } from "./token-estimator.js";
5
+ export function defaultCompileOptions() {
6
+ return {
7
+ redactPii: true,
8
+ includeProvenanceSnippets: false,
9
+ deltaMode: false
10
+ };
11
+ }
12
+ export function compileRecordContext(record, profileName, options) {
13
+ const { name, config } = resolveHeadlessProfile(profileName);
14
+ const resolvedOptions = {
15
+ ...defaultCompileOptions(),
16
+ ...(options ?? {})
17
+ };
18
+ const sections = [];
19
+ const keptFields = [];
20
+ if (config.keepSections.includes("summary") && record.summary) {
21
+ sections.push(`SUMMARY\n${record.summary}`);
22
+ keptFields.push("summary");
23
+ }
24
+ const fieldMap = {
25
+ customer: record.fields.account,
26
+ issue: record.fields.issue,
27
+ product: record.fields.product,
28
+ next_steps: record.fields.requested_outcome,
29
+ account: record.fields.account,
30
+ stakeholders: record.fields.primary_contact,
31
+ open_risks: record.fields.priority,
32
+ opportunities: record.fields.arr,
33
+ recent_activity: typeof record.metadata.recent_activity === "string"
34
+ ? record.metadata.recent_activity
35
+ : undefined
36
+ };
37
+ for (const section of config.keepSections) {
38
+ const value = fieldMap[section];
39
+ if (value) {
40
+ sections.push(`${section.toUpperCase()}\n${value}`);
41
+ keptFields.push(section);
42
+ }
43
+ }
44
+ if (config.keepSections.includes("key_entities") && record.entities.length > 0) {
45
+ const values = [...new Set(record.entities.slice(0, 8).map((entity) => entity.value))];
46
+ sections.push(`KEY_ENTITIES\n${values.join(", ")}`);
47
+ keptFields.push("key_entities");
48
+ }
49
+ if (config.keepSections.includes("action_items")) {
50
+ const action = record.fields.requested_outcome ?? "Review canonical record and proceed cautiously.";
51
+ sections.push(`ACTION_ITEMS\n- ${action}`);
52
+ keptFields.push("action_items");
53
+ }
54
+ if (resolvedOptions.includeProvenanceSnippets && record.provenance.length > 0) {
55
+ const snippets = record.provenance
56
+ .slice(0, 3)
57
+ .map((item) => `- ${item.evidence}`)
58
+ .join("\n");
59
+ sections.push(`PROVENANCE_SNIPPETS\n${snippets}`);
60
+ keptFields.push("provenance_snippets");
61
+ }
62
+ let compiledContext = sections.join("\n\n").trim();
63
+ if (resolvedOptions.redactPii) {
64
+ compiledContext = redactText(compiledContext);
65
+ }
66
+ const budget = resolvedOptions.maxTokensOverride ?? config.maxTokens;
67
+ compiledContext = truncateToBudget(compiledContext, budget);
68
+ const tokenEstimateAfter = estimateTokens(compiledContext);
69
+ const beforeText = `${record.summary} ${record.provenance.map((item) => item.evidence).join(" ")}`;
70
+ const tokenEstimateBefore = estimateTokens(beforeText);
71
+ const tokenReductionPct = tokenEstimateBefore === 0
72
+ ? 0
73
+ : Number((Math.max(0, (tokenEstimateBefore - tokenEstimateAfter) / tokenEstimateBefore) * 100).toFixed(2));
74
+ const completenessScore = Number(Math.min(1, (Object.keys(record.fields).length + record.entities.length * 0.15) / 8).toFixed(2));
75
+ const warnings = [];
76
+ if (!record.fields.issue) {
77
+ warnings.push("Issue field missing or not inferred.");
78
+ }
79
+ if (!record.fields.account) {
80
+ warnings.push("Account field missing or not inferred.");
81
+ }
82
+ if (resolvedOptions.deltaMode) {
83
+ warnings.push("Delta mode enabled: only compact context should be sent downstream.");
84
+ }
85
+ return {
86
+ profile: name,
87
+ compiledContext,
88
+ qcReport: {
89
+ completenessScore,
90
+ tokenEstimateBefore,
91
+ tokenEstimateAfter,
92
+ tokenReductionPct,
93
+ warnings,
94
+ keptFields
95
+ }
96
+ };
97
+ }
98
+ export function createCompileTraceId(record, compiledContext, policyDecision) {
99
+ return createHash("sha1")
100
+ .update(`${record.recordId}:${compiledContext}:${policyDecision.recommendedMode}`)
101
+ .digest("hex")
102
+ .slice(0, 16);
103
+ }
104
+ function truncateToBudget(text, budget) {
105
+ const words = text.split(/\s+/).filter(Boolean);
106
+ if (words.length <= budget) {
107
+ return text;
108
+ }
109
+ return words.slice(0, budget).join(" ");
110
+ }
111
+ //# sourceMappingURL=compile-context.js.map
@@ -0,0 +1,2 @@
1
+ import type { Entity } from "./types.js";
2
+ export declare function extractEntities(text: string): Entity[];
@@ -0,0 +1,44 @@
1
+ import { CANONICAL_FIELD_LABELS, toFieldKey } from "./labels.js";
2
+ const EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
3
+ const MONEY_RE = /\$\s?([0-9][0-9,]*)/g;
4
+ const DATE_RE = /\b20\d{2}-\d{2}-\d{2}\b/g;
5
+ export function extractEntities(text) {
6
+ const entities = [];
7
+ for (const match of text.matchAll(EMAIL_RE)) {
8
+ entities.push({
9
+ type: "email",
10
+ value: match[0],
11
+ confidence: 0.95
12
+ });
13
+ }
14
+ for (const match of text.matchAll(MONEY_RE)) {
15
+ entities.push({
16
+ type: "money",
17
+ value: match[0],
18
+ confidence: 0.8
19
+ });
20
+ }
21
+ for (const match of text.matchAll(DATE_RE)) {
22
+ entities.push({
23
+ type: "date",
24
+ value: match[0],
25
+ confidence: 0.9
26
+ });
27
+ }
28
+ for (const label of CANONICAL_FIELD_LABELS) {
29
+ const pattern = new RegExp(`${escapeForRegex(label)}:\\s*(.+)`, "im");
30
+ const match = pattern.exec(text);
31
+ if (match?.[1]) {
32
+ entities.push({
33
+ type: toFieldKey(label),
34
+ value: match[1].trim(),
35
+ confidence: 0.9
36
+ });
37
+ }
38
+ }
39
+ return entities;
40
+ }
41
+ function escapeForRegex(value) {
42
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
43
+ }
44
+ //# sourceMappingURL=entities.js.map
@@ -0,0 +1,2 @@
1
+ import type { CanonicalRecord, PolicyDecision, QcReport } from "./types.js";
2
+ export declare function evaluatePolicy(record: CanonicalRecord, qcReport: QcReport, profileName: string | undefined): PolicyDecision;
@@ -0,0 +1,42 @@
1
+ import { resolveHeadlessProfile } from "./profiles.js";
2
+ import { classifySensitivity } from "./sensitivity.js";
3
+ export function evaluatePolicy(record, qcReport, profileName) {
4
+ const { name, config } = resolveHeadlessProfile(profileName);
5
+ const reasons = [];
6
+ let allowAutonomy = true;
7
+ let severity = "low";
8
+ let recommendedMode = "auto";
9
+ let requiresApproval = false;
10
+ if (qcReport.completenessScore < config.autonomyThreshold) {
11
+ allowAutonomy = false;
12
+ severity = "medium";
13
+ recommendedMode = "human_review";
14
+ reasons.push("Completeness below autonomy threshold.");
15
+ }
16
+ if (qcReport.warnings.length >= 2) {
17
+ allowAutonomy = false;
18
+ severity = "high";
19
+ recommendedMode = "human_review";
20
+ reasons.push("Too many unresolved warnings in compiled context.");
21
+ }
22
+ const sensitivity = classifySensitivity(record);
23
+ if (sensitivity.level === "high") {
24
+ allowAutonomy = false;
25
+ severity = "high";
26
+ recommendedMode = "approval_required";
27
+ requiresApproval = true;
28
+ reasons.push(...sensitivity.reasons);
29
+ }
30
+ if (reasons.length === 0) {
31
+ reasons.push("Record meets baseline autonomy policy.");
32
+ }
33
+ return {
34
+ profile: name,
35
+ allowAutonomy,
36
+ severity,
37
+ reasons,
38
+ recommendedMode,
39
+ requiresApproval
40
+ };
41
+ }
42
+ //# sourceMappingURL=evaluate-policy.js.map
@@ -0,0 +1,11 @@
1
+ export type { ApiKey, ApiKeyScope, CanonicalRecord, CompileOptions, CompileTrace, ContextPack, Entity, PolicyDecision, PolicyMode, PolicySeverity, ProvenanceItem, QcReport, SensitivityClassification, SensitivityLevel, SyncPreviewInput, SyncPreviewResponse, Tenant } from "./types.js";
2
+ export { HEADLESSOS_PROFILES, resolveHeadlessProfile, type HeadlessCompileProfile, type HeadlessProfileName, type HeadlessProfileSection } from "./profiles.js";
3
+ export { CANONICAL_FIELD_LABELS, toFieldKey, type CanonicalFieldLabel } from "./labels.js";
4
+ export { normalizeTextRecord, extractLabeledFields, type NormalizeTextInput } from "./normalizer.js";
5
+ export { extractEntities } from "./entities.js";
6
+ export { redactText } from "./redaction.js";
7
+ export { compileRecordContext, createCompileTraceId, defaultCompileOptions } from "./compile-context.js";
8
+ export { DEFAULT_COST_PER_1K_TOKENS, estimateCostUsd, estimateTokens } from "./token-estimator.js";
9
+ export { classifySensitivity } from "./sensitivity.js";
10
+ export { evaluatePolicy } from "./evaluate-policy.js";
11
+ export { buildSyncPreview } from "./sync-preview.js";
@@ -0,0 +1,24 @@
1
+ // ─── Context Flow: OSS data pipeline for AI agents ──────────────────────────
2
+ // Transforms raw unstructured records into clean, PII-safe, token-budgeted
3
+ // context packs ready for any LLM agent. No infrastructure required.
4
+ // Profiles
5
+ export { HEADLESSOS_PROFILES, resolveHeadlessProfile } from "./profiles.js";
6
+ // Labels
7
+ export { CANONICAL_FIELD_LABELS, toFieldKey } from "./labels.js";
8
+ // Normalizer (raw text → canonical record)
9
+ export { normalizeTextRecord, extractLabeledFields } from "./normalizer.js";
10
+ // Entity extraction
11
+ export { extractEntities } from "./entities.js";
12
+ // PII redaction
13
+ export { redactText } from "./redaction.js";
14
+ // Context compilation (canonical record → context pack)
15
+ export { compileRecordContext, createCompileTraceId, defaultCompileOptions } from "./compile-context.js";
16
+ // Token estimation
17
+ export { DEFAULT_COST_PER_1K_TOKENS, estimateCostUsd, estimateTokens } from "./token-estimator.js";
18
+ // Sensitivity classification
19
+ export { classifySensitivity } from "./sensitivity.js";
20
+ // Policy evaluation
21
+ export { evaluatePolicy } from "./evaluate-policy.js";
22
+ // Sync preview
23
+ export { buildSyncPreview } from "./sync-preview.js";
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ export declare const CANONICAL_FIELD_LABELS: readonly ["Account", "Primary Contact", "Product", "Issue", "Priority", "ARR", "Renewal Date", "Requested Outcome", "Recent Activity", "Owner", "Status"];
2
+ export type CanonicalFieldLabel = (typeof CANONICAL_FIELD_LABELS)[number];
3
+ export declare function toFieldKey(label: CanonicalFieldLabel | string): string;
@@ -0,0 +1,17 @@
1
+ export const CANONICAL_FIELD_LABELS = [
2
+ "Account",
3
+ "Primary Contact",
4
+ "Product",
5
+ "Issue",
6
+ "Priority",
7
+ "ARR",
8
+ "Renewal Date",
9
+ "Requested Outcome",
10
+ "Recent Activity",
11
+ "Owner",
12
+ "Status"
13
+ ];
14
+ export function toFieldKey(label) {
15
+ return label.toLowerCase().replace(/\s+/g, "_");
16
+ }
17
+ //# sourceMappingURL=labels.js.map
@@ -0,0 +1,9 @@
1
+ import type { CanonicalRecord } from "./types.js";
2
+ export interface NormalizeTextInput {
3
+ text: string;
4
+ sourceName?: string;
5
+ recordType?: string;
6
+ metadata?: Record<string, unknown>;
7
+ }
8
+ export declare function extractLabeledFields(text: string): Record<string, string>;
9
+ export declare function normalizeTextRecord(input: NormalizeTextInput): CanonicalRecord;
@@ -0,0 +1,69 @@
1
+ import { createHash } from "node:crypto";
2
+ import { extractEntities } from "./entities.js";
3
+ import { CANONICAL_FIELD_LABELS, toFieldKey } from "./labels.js";
4
+ export function extractLabeledFields(text) {
5
+ const fields = {};
6
+ for (const label of CANONICAL_FIELD_LABELS) {
7
+ const match = new RegExp(`${escapeForRegex(label)}:\\s*(.+)`, "im").exec(text);
8
+ if (match?.[1]) {
9
+ fields[toFieldKey(label)] = match[1].trim();
10
+ }
11
+ }
12
+ return fields;
13
+ }
14
+ export function normalizeTextRecord(input) {
15
+ const sourceName = input.sourceName ?? "inline";
16
+ const recordType = input.recordType ?? "note";
17
+ const metadata = {
18
+ ...(input.metadata ?? {})
19
+ };
20
+ const lines = input.text
21
+ .split(/\r?\n/)
22
+ .map((line) => line.trim())
23
+ .filter((line) => line.length > 0);
24
+ const fields = extractLabeledFields(input.text);
25
+ const summary = buildSummary(fields, lines);
26
+ const recordId = createHash("sha1")
27
+ .update(`${sourceName}:${recordType}:${input.text}`)
28
+ .digest("hex")
29
+ .slice(0, 12);
30
+ const provenance = lines.slice(0, 10).map((line) => ({
31
+ sourceName,
32
+ evidence: line
33
+ }));
34
+ const entities = extractEntities(input.text);
35
+ if (fields.recent_activity) {
36
+ metadata.recent_activity = fields.recent_activity;
37
+ }
38
+ return {
39
+ recordId,
40
+ recordType,
41
+ summary,
42
+ fields,
43
+ entities,
44
+ provenance,
45
+ metadata
46
+ };
47
+ }
48
+ function buildSummary(fields, lines) {
49
+ const summaryParts = [];
50
+ for (const key of ["account", "product", "issue", "requested_outcome"]) {
51
+ if (fields[key]) {
52
+ summaryParts.push(`${toTitleCase(key)}: ${fields[key]}`);
53
+ }
54
+ }
55
+ if (summaryParts.length > 0) {
56
+ return summaryParts.join(" | ");
57
+ }
58
+ return lines.slice(0, 3).join(" ").slice(0, 400);
59
+ }
60
+ function toTitleCase(value) {
61
+ return value
62
+ .split("_")
63
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
64
+ .join(" ");
65
+ }
66
+ function escapeForRegex(value) {
67
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
68
+ }
69
+ //# sourceMappingURL=normalizer.js.map
@@ -0,0 +1,33 @@
1
+ export type HeadlessProfileSection = "summary" | "customer" | "issue" | "product" | "next_steps" | "account" | "stakeholders" | "open_risks" | "opportunities" | "recent_activity" | "key_entities" | "action_items" | "provenance_snippets";
2
+ export interface HeadlessCompileProfile {
3
+ maxTokens: number;
4
+ keepSections: readonly HeadlessProfileSection[];
5
+ autonomyThreshold: number;
6
+ }
7
+ export declare const HEADLESSOS_PROFILES: {
8
+ support_triage: {
9
+ maxTokens: number;
10
+ keepSections: ("summary" | "customer" | "issue" | "product" | "next_steps")[];
11
+ autonomyThreshold: number;
12
+ };
13
+ revops_account_brief: {
14
+ maxTokens: number;
15
+ keepSections: ("account" | "stakeholders" | "open_risks" | "opportunities" | "recent_activity")[];
16
+ autonomyThreshold: number;
17
+ };
18
+ generic_agent_small: {
19
+ maxTokens: number;
20
+ keepSections: ("summary" | "key_entities" | "action_items")[];
21
+ autonomyThreshold: number;
22
+ };
23
+ martin360_agent_stage: {
24
+ maxTokens: number;
25
+ keepSections: ("summary" | "issue" | "next_steps" | "account" | "stakeholders" | "key_entities")[];
26
+ autonomyThreshold: number;
27
+ };
28
+ };
29
+ export type HeadlessProfileName = keyof typeof HEADLESSOS_PROFILES;
30
+ export declare function resolveHeadlessProfile(profile: HeadlessProfileName | string | undefined): {
31
+ name: HeadlessProfileName;
32
+ config: HeadlessCompileProfile;
33
+ };
@@ -0,0 +1,36 @@
1
+ export const HEADLESSOS_PROFILES = {
2
+ support_triage: {
3
+ maxTokens: 800,
4
+ keepSections: ["summary", "customer", "issue", "product", "next_steps"],
5
+ autonomyThreshold: 0.8
6
+ },
7
+ revops_account_brief: {
8
+ maxTokens: 1200,
9
+ keepSections: ["account", "stakeholders", "open_risks", "opportunities", "recent_activity"],
10
+ autonomyThreshold: 0.85
11
+ },
12
+ generic_agent_small: {
13
+ maxTokens: 600,
14
+ keepSections: ["summary", "key_entities", "action_items"],
15
+ autonomyThreshold: 0.9
16
+ },
17
+ martin360_agent_stage: {
18
+ maxTokens: 900,
19
+ keepSections: ["summary", "account", "issue", "stakeholders", "next_steps", "key_entities"],
20
+ autonomyThreshold: 0.82
21
+ }
22
+ };
23
+ export function resolveHeadlessProfile(profile) {
24
+ if (profile && profile in HEADLESSOS_PROFILES) {
25
+ const name = profile;
26
+ return {
27
+ name,
28
+ config: HEADLESSOS_PROFILES[name]
29
+ };
30
+ }
31
+ return {
32
+ name: "generic_agent_small",
33
+ config: HEADLESSOS_PROFILES.generic_agent_small
34
+ };
35
+ }
36
+ //# sourceMappingURL=profiles.js.map
@@ -0,0 +1 @@
1
+ export declare function redactText(text: string): string;
@@ -0,0 +1,6 @@
1
+ const EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
2
+ const PHONE_RE = /(?:\+?\d{1,3}[ -]?)?(?:\(?\d{3}\)?[ -]?)?\d{3}[ -]?\d{4}/g;
3
+ export function redactText(text) {
4
+ return text.replace(EMAIL_RE, "[REDACTED_EMAIL]").replace(PHONE_RE, "[REDACTED_PHONE]");
5
+ }
6
+ //# sourceMappingURL=redaction.js.map
@@ -0,0 +1,2 @@
1
+ import type { CanonicalRecord, SensitivityClassification } from "./types.js";
2
+ export declare function classifySensitivity(record: CanonicalRecord): SensitivityClassification;
@@ -0,0 +1,27 @@
1
+ const HIGH_RISK_RECORD_TYPES = new Set(["security_incident", "finance_change"]);
2
+ export function classifySensitivity(record) {
3
+ const reasons = [];
4
+ const matchedEntityTypes = [...new Set(record.entities.map((entity) => entity.type))];
5
+ let level = "low";
6
+ if (HIGH_RISK_RECORD_TYPES.has(record.recordType)) {
7
+ level = "high";
8
+ reasons.push(`Sensitive record type "${record.recordType}" requires approval.`);
9
+ }
10
+ if (matchedEntityTypes.includes("money")) {
11
+ level = level === "high" ? "high" : "moderate";
12
+ reasons.push("Financial values were detected in the source record.");
13
+ }
14
+ if (matchedEntityTypes.includes("email")) {
15
+ level = level === "high" ? "high" : "moderate";
16
+ reasons.push("Direct contact data was detected in the source record.");
17
+ }
18
+ if (reasons.length === 0) {
19
+ reasons.push("No elevated sensitivity indicators were detected.");
20
+ }
21
+ return {
22
+ level,
23
+ reasons,
24
+ matchedEntityTypes
25
+ };
26
+ }
27
+ //# sourceMappingURL=sensitivity.js.map
@@ -0,0 +1,2 @@
1
+ import type { SyncPreviewInput, SyncPreviewResponse } from "./types.js";
2
+ export declare function buildSyncPreview(input: SyncPreviewInput): SyncPreviewResponse;